/
githubmr
/
facebook-react
Обзор
Документация
Войти
/
githubmr
/
facebook-react
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
main
compiler/apps/playground/components/Editor/ConfigEditor.tsx
215 строк
7 KB
Eugene Choi
[playground] Config panel quality fixes (#34611)
03 окт 2025, 17:52
Не верифицирован
03 окт 2025, 17:52
0eebd37
Код
Авторство
О чём код?
/** * 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. */ import MonacoEditor, {loader, type Monaco} from '@monaco-editor/react'; import type {editor} from 'monaco-editor'; import * as monaco from 'monaco-editor'; import React, { useState, useRef, unstable_ViewTransition as ViewTransition, unstable_addTransitionType as addTransitionType, startTransition, } from 'react'; import {Resizable} from 're-resizable'; import {useStore, useStoreDispatch} from '../StoreContext'; import {monacoConfigOptions} from './monacoOptions'; import {IconChevron} from '../Icons/IconChevron'; import {CONFIG_PANEL_TRANSITION} from '../../lib/transitionTypes'; // @ts-expect-error - webpack asset/source loader handles .d.ts files as strings import compilerTypeDefs from 'babel-plugin-react-compiler/dist/index.d.ts'; loader.config({monaco}); export default function ConfigEditor({ formattedAppliedConfig, }: { formattedAppliedConfig: string; }): React.ReactElement { const [isExpanded, setIsExpanded] = useState(false); return ( // TODO: Use <Activity> when it is compatible with Monaco: https://github.com/suren-atoyan/monaco-react/issues/753 <> <div style={{ display: isExpanded ? 'block' : 'none', }}> <ExpandedEditor onToggle={() => { startTransition(() => { addTransitionType(CONFIG_PANEL_TRANSITION); setIsExpanded(false); }); }} formattedAppliedConfig={formattedAppliedConfig} /> </div> <div style={{ display: !isExpanded ? 'block' : 'none', }}> <CollapsedEditor onToggle={() => { startTransition(() => { addTransitionType(CONFIG_PANEL_TRANSITION); setIsExpanded(true); }); }} /> </div> </> ); } function ExpandedEditor({ onToggle, formattedAppliedConfig, }: { onToggle: (expanded: boolean) => void; formattedAppliedConfig: string; }): React.ReactElement { const store = useStore(); const dispatchStore = useStoreDispatch(); const debounceTimerRef = useRef<NodeJS.Timeout | null>(null); const handleChange: (value: string | undefined) => void = ( value: string | undefined, ) => { if (value === undefined) return; if (debounceTimerRef.current) { clearTimeout(debounceTimerRef.current); } debounceTimerRef.current = setTimeout(() => { dispatchStore({ type: 'updateConfig', payload: { config: value, }, }); }, 500); // 500ms debounce delay }; const handleMount: ( _: editor.IStandaloneCodeEditor, monaco: Monaco, ) => void = (_, monaco) => { // Add the babel-plugin-react-compiler type definitions to Monaco monaco.languages.typescript.typescriptDefaults.addExtraLib( //@ts-expect-error - compilerTypeDefs is a string compilerTypeDefs, 'file:///node_modules/babel-plugin-react-compiler/dist/index.d.ts', ); monaco.languages.typescript.typescriptDefaults.setCompilerOptions({ target: monaco.languages.typescript.ScriptTarget.Latest, allowNonTsExtensions: true, moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs, module: monaco.languages.typescript.ModuleKind.ESNext, noEmit: true, strict: false, esModuleInterop: true, allowSyntheticDefaultImports: true, jsx: monaco.languages.typescript.JsxEmit.React, }); }; return ( <ViewTransition update={{[CONFIG_PANEL_TRANSITION]: 'slide-in', default: 'none'}}> <Resizable minWidth={300} maxWidth={600} defaultSize={{width: 350}} enable={{right: true, bottom: false}}> <div className="bg-blue-10 relative h-full flex flex-col !h-[calc(100vh_-_3.5rem)] border border-gray-300"> <div className="absolute w-8 h-16 bg-blue-10 rounded-r-full flex items-center justify-center z-[2] cursor-pointer border border-l-0 border-gray-300" title="Minimize config editor" onClick={onToggle} style={{ top: '50%', marginTop: '-32px', right: '-32px', borderTopLeftRadius: 0, borderBottomLeftRadius: 0, }}> <IconChevron displayDirection="left" className="text-blue-50" /> </div> <div className="flex-1 flex flex-col m-2 mb-2"> <div className="pb-2"> <h2 className="inline-block text-blue-50 py-1.5 px-1.5 xs:px-3 sm:px-4 text-sm"> Config Overrides </h2> </div> <div className="flex-1 border border-gray-300"> <MonacoEditor path={'config.ts'} language={'typescript'} value={store.config} onMount={handleMount} onChange={handleChange} loading={''} className="monaco-editor-config" options={monacoConfigOptions} /> </div> </div> <div className="flex-1 flex flex-col m-2"> <div className="pb-2"> <h2 className="inline-block text-blue-50 py-1.5 px-1.5 xs:px-3 sm:px-4 text-sm"> Applied Configs </h2> </div> <div className="flex-1 border border-gray-300"> <MonacoEditor path={'applied-config.js'} language={'javascript'} value={formattedAppliedConfig} loading={''} className="monaco-editor-applied-config" options={{ ...monacoConfigOptions, readOnly: true, }} /> </div> </div> </div> </Resizable> </ViewTransition> ); } function CollapsedEditor({ onToggle, }: { onToggle: () => void; }): React.ReactElement { return ( <div className="w-4 !h-[calc(100vh_-_3.5rem)]" style={{position: 'relative'}}> <div className="absolute w-10 h-16 bg-blue-10 hover:translate-x-2 transition-transform rounded-r-full flex items-center justify-center z-[2] cursor-pointer border border-gray-300" title="Expand config editor" onClick={onToggle} style={{ top: '50%', marginTop: '-32px', left: '-8px', borderTopLeftRadius: 0, borderBottomLeftRadius: 0, }}> <IconChevron displayDirection="right" className="text-blue-50" /> </div> </div> ); }