/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import * as React from 'react';
import {useCallback, useContext, useEffect, useMemo, useRef} from 'react';
import {SettingsModalContext} from './SettingsModalContext';
import Button from '../Button';
import ButtonIcon from '../ButtonIcon';
import TabBar from '../TabBar';
import {StoreContext} from '../context';
import {
useLocalStorage,
useModalDismissSignal,
useSubscription,
} from '../hooks';
import ComponentsSettings from './ComponentsSettings';
import DebuggingSettings from './DebuggingSettings';
import GeneralSettings from './GeneralSettings';
import ProfilerSettings from './ProfilerSettings';
import styles from './SettingsModal.css';
type TabID = 'general' | 'components' | 'profiler';
export default function SettingsModal(_: {}): React.Node {
const {isModalShowing, setIsModalShowing} = useContext(SettingsModalContext);
const store = useContext(StoreContext);
const {profilerStore} = store;
// Updating preferences while profiling is in progress could break things (e.g. filtering)
// Explicitly disallow it for now.
const isProfilingSubscription = useMemo(
() => ({
getCurrentValue: () => profilerStore.isProfiling,
subscribe: (callback: Function) => {
profilerStore.addListener('isProfiling', callback);
return () => profilerStore.removeListener('isProfiling', callback);
},
}),
[profilerStore],
);
const isProfiling = useSubscription<boolean>(isProfilingSubscription);
if (isProfiling && isModalShowing) {
setIsModalShowing(false);
}
if (!isModalShowing) {
return null;
}
return <SettingsModalImpl />;
}
function SettingsModalImpl(_: {}) {
const {setIsModalShowing} = useContext(SettingsModalContext);
const dismissModal = useCallback(
() => setIsModalShowing(false),
[setIsModalShowing],
);
const [selectedTabID, selectTab] = useLocalStorage<TabID>(
'React::DevTools::selectedSettingsTabID',
'general',
);
const modalRef = useRef<HTMLDivElement | null>(null);
useModalDismissSignal(modalRef, dismissModal);
useEffect(() => {
if (modalRef.current !== null) {
modalRef.current.focus();
}
}, [modalRef]);
let view = null;
switch (selectedTabID) {
case 'components':
view = <ComponentsSettings />;
break;
// $FlowFixMe[incompatible-type] is this missing in TabID?
case 'debugging':
view = <DebuggingSettings />;
break;
case 'general':
view = <GeneralSettings />;
break;
case 'profiler':
view = <ProfilerSettings />;
break;
default:
break;
}
return (
<div className={styles.Background}>
<div className={styles.Modal} ref={modalRef}>
<div className={styles.Tabs}>
<TabBar
currentTab={selectedTabID}
id="Settings"
selectTab={selectTab}
tabs={tabs}
type="settings"
/>
<div className={styles.Spacer} />
<Button onClick={dismissModal} title="Close settings dialog">
<ButtonIcon type="close" />
</Button>
</div>
<div className={styles.Content}>{view}</div>
</div>
</div>
);
}
const tabs = [
{
id: 'general',
icon: 'settings',
label: 'General',
},
{
id: 'debugging',
icon: 'bug',
label: 'Debugging',
},
{
id: 'components',
icon: 'components',
label: 'Components',
},
{
id: 'profiler',
icon: 'profiler',
label: 'Profiler',
},
];