Skip to content

Commit e964dd9

Browse files
committed
(wip) API 選択をドロップダウンにする
1 parent 8debbab commit e964dd9

File tree

2 files changed

+52
-135
lines changed

2 files changed

+52
-135
lines changed

src/components/DebugPane/Api.tsx

Lines changed: 50 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import React, { useEffect, useRef, useState } from 'react'
2-
import { Button, Col, FormControl, Row } from 'react-bootstrap'
1+
import React, { useEffect, useMemo, useRef, useState } from 'react'
2+
import { Button, Dropdown, FormControl } from 'react-bootstrap'
33

44
import { useSoraDevtoolsStore } from '@/app/store'
55
import { API_TEMPLATES } from '@/constants'
@@ -8,6 +8,9 @@ import { JSONInputField } from '@/components/DevtoolsPane/JSONInputField.tsx'
88

99
import { JsonTree } from './JsonTree.tsx'
1010

11+
type ApiTemplate = (typeof API_TEMPLATES)[number]
12+
type ApiTemplateGroups = Record<string, ApiTemplate[]>
13+
1114
const ClearButton = React.memo(() => {
1215
const onClick = (): void => {
1316
useSoraDevtoolsStore.getState().clearApiObjects()
@@ -25,8 +28,8 @@ type ApiFormProps = {
2528
selectedMethod: string
2629
params: string
2730
setParams: (params: string) => void
28-
setShowModal: (show: boolean) => void
29-
buttonRef: React.RefObject<HTMLButtonElement | null>
31+
templateGroups: ApiTemplateGroups
32+
onSelectMethod: (method: string, methodParams?: Record<string, unknown> | unknown[]) => void
3033
}
3134

3235
const ApiForm: React.FC<ApiFormProps> = ({
@@ -35,8 +38,8 @@ const ApiForm: React.FC<ApiFormProps> = ({
3538
selectedMethod,
3639
params,
3740
setParams,
38-
setShowModal,
39-
buttonRef,
41+
templateGroups,
42+
onSelectMethod,
4043
}) => {
4144
const urlRef = useRef<HTMLInputElement>(null)
4245
const timeoutRef = useRef<HTMLInputElement>(null)
@@ -240,14 +243,37 @@ const ApiForm: React.FC<ApiFormProps> = ({
240243
<div className="mb-1" style={{ color: '#fff' }}>
241244
<strong>method:</strong>
242245
</div>
243-
<Button
244-
ref={buttonRef}
245-
variant="secondary"
246-
onClick={() => setShowModal(true)}
247-
style={{ width: '100%', fontSize: '1rem', fontWeight: 'bold' }}
248-
>
249-
{selectedMethod || 'Select method'}
250-
</Button>
246+
<Dropdown className="w-100">
247+
<Dropdown.Toggle
248+
id="debug-api-method-dropdown"
249+
variant="secondary"
250+
style={{ width: '100%', fontSize: '1rem', fontWeight: 'bold', textAlign: 'left' }}
251+
>
252+
{selectedMethod || 'Select method'}
253+
</Dropdown.Toggle>
254+
<Dropdown.Menu
255+
className="w-100"
256+
style={{ maxHeight: '420px', overflowY: 'auto', minWidth: '480px', width: '100%' }}
257+
data-testid="debug-api-method-menu"
258+
>
259+
{Object.entries(templateGroups).map(([groupName, templates], index, entries) => (
260+
<React.Fragment key={groupName}>
261+
<Dropdown.Header>{groupName}</Dropdown.Header>
262+
{templates.map((template) => (
263+
<Dropdown.Item
264+
key={template.method}
265+
active={selectedMethod === template.method}
266+
onClick={() => onSelectMethod(template.method, template.params)}
267+
style={{ fontWeight: 'bold' }}
268+
>
269+
{template.method.replace('Sora_', '')}
270+
</Dropdown.Item>
271+
))}
272+
{index < entries.length - 1 && <Dropdown.Divider />}
273+
</React.Fragment>
274+
))}
275+
</Dropdown.Menu>
276+
</Dropdown>
251277
</div>
252278

253279
<div style={{ width: '150px' }}>
@@ -552,26 +578,14 @@ export const Api: React.FC = () => {
552578
}
553579
const [selectedMethod, setSelectedMethod] = useState('')
554580
const [params, setParams] = useState('')
555-
const [showModal, setShowModal] = useState(false)
556-
const buttonRef = useRef<HTMLButtonElement>(null)
557-
const [modalTop, setModalTop] = useState(0)
558-
const [modalLeft, setModalLeft] = useState(0)
559-
const [modalWidth, setModalWidth] = useState(0)
560-
561-
// ボタンの位置が変わったときにモーダルの位置を更新
562-
useEffect(() => {
563-
if (showModal && buttonRef.current) {
564-
const rect = buttonRef.current.getBoundingClientRect()
565-
// API タブページの幅を取得するため、親要素を探す
566-
const container = buttonRef.current.closest('[style*="position: relative"]')
567-
if (container) {
568-
const containerRect = container.getBoundingClientRect()
569-
setModalTop(rect.bottom + 4)
570-
setModalLeft(containerRect.left)
571-
setModalWidth(containerRect.width)
572-
}
573-
}
574-
}, [showModal])
581+
const templateGroups = useMemo(() => {
582+
return API_TEMPLATES.reduce((acc: ApiTemplateGroups, template) => {
583+
const group = template.group || 'Other'
584+
if (!acc[group]) acc[group] = []
585+
acc[group].push(template)
586+
return acc
587+
}, {})
588+
}, [])
575589

576590
const handleReuse = (apiObject: ApiObject): void => {
577591
setUrl(apiObject.url)
@@ -595,113 +609,18 @@ export const Api: React.FC = () => {
595609
} else {
596610
setParams('')
597611
}
598-
setShowModal(false)
599612
}
600613

601614
return (
602615
<div style={{ position: 'relative', height: '100%', display: 'flex', flexDirection: 'column' }}>
603-
{showModal && (
604-
<div
605-
style={{
606-
position: 'fixed',
607-
top: 0,
608-
left: 0,
609-
right: 0,
610-
bottom: 0,
611-
backgroundColor: 'rgba(0, 0, 0, 0.5)',
612-
zIndex: 998,
613-
}}
614-
onClick={() => setShowModal(false)}
615-
onKeyDown={(e) => {
616-
if (e.key === 'Escape') {
617-
setShowModal(false)
618-
}
619-
}}
620-
/>
621-
)}
622-
{showModal && (
623-
<div
624-
style={{
625-
position: 'fixed',
626-
top: `${modalTop}px`,
627-
left: `${modalLeft}px`,
628-
width: `${modalWidth}px`,
629-
backgroundColor: '#1a1a1a',
630-
border: '1px solid #444',
631-
borderRadius: '8px',
632-
maxHeight: `calc(100vh - ${modalTop}px - 20px)`,
633-
overflowY: 'auto',
634-
zIndex: 1000,
635-
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.3)',
636-
padding: '20px',
637-
}}
638-
>
639-
<Button
640-
variant="outline-light"
641-
size="sm"
642-
onClick={() => setShowModal(false)}
643-
style={{
644-
position: 'absolute',
645-
top: '10px',
646-
right: '10px',
647-
zIndex: 1,
648-
}}
649-
>
650-
×
651-
</Button>
652-
{(() => {
653-
type TemplateType = (typeof API_TEMPLATES)[number]
654-
const groups = API_TEMPLATES.reduce((acc: Record<string, TemplateType[]>, template) => {
655-
const group = template.group || 'Other'
656-
if (!acc[group]) acc[group] = []
657-
acc[group].push(template)
658-
return acc
659-
}, {})
660-
661-
return Object.entries(groups).map(([groupName, templates]) => (
662-
<div key={groupName} className="mb-4">
663-
<div
664-
style={{
665-
color: '#fff',
666-
fontWeight: 'bold',
667-
marginBottom: '12px',
668-
fontSize: '1.1rem',
669-
}}
670-
>
671-
{groupName}
672-
</div>
673-
<Row>
674-
{templates.map((template) => (
675-
<Col key={template.method} xs={6} className="mb-2">
676-
<Button
677-
variant={selectedMethod === template.method ? 'primary' : 'secondary'}
678-
size="sm"
679-
style={{
680-
width: '100%',
681-
fontSize: '1.1rem',
682-
padding: '12px',
683-
fontWeight: 'bold',
684-
}}
685-
onClick={() => handleMethodSelect(template.method, template.params)}
686-
>
687-
{template.method.replace('Sora_', '')}
688-
</Button>
689-
</Col>
690-
))}
691-
</Row>
692-
</div>
693-
))
694-
})()}
695-
</div>
696-
)}
697616
<ApiForm
698617
url={url}
699618
setUrl={setUrl}
700619
selectedMethod={selectedMethod}
701620
params={params}
702621
setParams={setParams}
703-
setShowModal={setShowModal}
704-
buttonRef={buttonRef}
622+
templateGroups={templateGroups}
623+
onSelectMethod={handleMethodSelect}
705624
/>
706625
{apiObjects.length > 0 && (
707626
<>

src/components/DebugPane/index.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Tab, Tabs } from 'react-bootstrap'
44
import { setDebugType } from '@/app/actions'
55
import { useSoraDevtoolsStore } from '@/app/store'
66

7-
// import { Api } from './Api.tsx'
7+
import { Api } from './Api.tsx'
88
import { CapabilitiesCodec } from './CapabilitiesCodec.tsx'
99
import { DataChannelMessagingMessages } from './DataChannelMessagingMessages.tsx'
1010
import { DebugFilter } from './Filter.tsx'
@@ -32,7 +32,7 @@ export const DebugPane: React.FC = () => {
3232
key === 'timeline' ||
3333
key === 'signaling' ||
3434
key === 'messaging' ||
35-
// key === 'api' ||
35+
key === 'api' ||
3636
key === 'rpc' ||
3737
key === 'codec'
3838
) {
@@ -75,11 +75,9 @@ export const DebugPane: React.FC = () => {
7575
<SendDataChannelMessagingMessage />
7676
<DataChannelMessagingMessages />
7777
</Tab>
78-
{/*
7978
<Tab eventKey="api" title="API">
8079
<Api />
8180
</Tab>
82-
*/}
8381
<Tab eventKey="rpc" title="RPC">
8482
<Rpc />
8583
</Tab>

0 commit comments

Comments
 (0)