From 68a5f5ca2f42c18e96695f7421958d3380a7421f Mon Sep 17 00:00:00 2001 From: Alessio Gravili Date: Sat, 28 Dec 2024 12:09:51 -0700 Subject: [PATCH] feat(ui): expose onMount to CodeField (#10223) This is useful for modifying the monaco instance, e.g. to add external types to the editor: ```tsx { console.log('editor mounted') monaco.languages.typescript.typescriptDefaults.setCompilerOptions({ allowNonTsExtensions: true, moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs, paths: { payload: ['file:///node_modules/payload/index.d.ts'], }, target: monaco.languages.typescript.ScriptTarget.ESNext, typeRoots: ['node_modules/@types', 'node_modules/payload'], }) const run = async () => { const types = await fetch('https://unpkg.com/payload@latest/dist/index.d.ts') const typesText = await types.text() monaco.languages.typescript.typescriptDefaults.addExtraLib( typesText, 'file:///node_modules/payload/index.d.ts', ) } void run() }} path={path} permissions={permissions} readOnly={readOnly} renderedBlocks={renderedBlocks} schemaPath={schemaPath} validate={validate} /> ``` --- packages/payload/src/admin/fields/Code.ts | 2 + packages/ui/src/fields/Code/index.tsx | 2 + .../collections/Posts/CodeFields.tsx | 85 +++++++++++++++++-- 3 files changed, 83 insertions(+), 6 deletions(-) diff --git a/packages/payload/src/admin/fields/Code.ts b/packages/payload/src/admin/fields/Code.ts index c58d3fd71a..e7409b29d3 100644 --- a/packages/payload/src/admin/fields/Code.ts +++ b/packages/payload/src/admin/fields/Code.ts @@ -1,3 +1,4 @@ +import type { EditorProps } from '@monaco-editor/react' import type { MarkOptional } from 'ts-essentials' import type { CodeField, CodeFieldClient } from '../../fields/config/types.js' @@ -20,6 +21,7 @@ type CodeFieldClientWithoutType = MarkOptional type CodeFieldBaseClientProps = { readonly autoComplete?: string + readonly onMount?: EditorProps['onMount'] readonly path: string readonly validate?: CodeFieldValidation } diff --git a/packages/ui/src/fields/Code/index.tsx b/packages/ui/src/fields/Code/index.tsx index 8f071eed06..078b124594 100644 --- a/packages/ui/src/fields/Code/index.tsx +++ b/packages/ui/src/fields/Code/index.tsx @@ -30,6 +30,7 @@ const CodeFieldComponent: CodeFieldClientComponent = (props) => { localized, required, }, + onMount, path, readOnly, validate, @@ -84,6 +85,7 @@ const CodeFieldComponent: CodeFieldClientComponent = (props) => { null : (val) => setValue(val)} + onMount={onMount} options={editorOptions} readOnly={readOnly} value={(value as string) || ''} diff --git a/test/lexical-mdx/collections/Posts/CodeFields.tsx b/test/lexical-mdx/collections/Posts/CodeFields.tsx index 287d952e00..c6d1bb2675 100644 --- a/test/lexical-mdx/collections/Posts/CodeFields.tsx +++ b/test/lexical-mdx/collections/Posts/CodeFields.tsx @@ -1,6 +1,6 @@ 'use client' -import type { CodeFieldClientProps } from 'payload' +import type { CodeFieldClient, CodeFieldClientProps } from 'payload' import { CodeField, useFormFields } from '@payloadcms/ui' import React, { useMemo } from 'react' @@ -13,7 +13,17 @@ const languageKeyToMonacoLanguageMap = { tsx: 'typescript', } -export const Code: React.FC = ({ field }) => { +export const Code: React.FC = ({ + autoComplete, + field, + forceRender, + path, + permissions, + readOnly, + renderedBlocks, + schemaPath, + validate, +}) => { const languageField = useFormFields(([fields]) => fields['language']) const language: string = @@ -21,13 +31,39 @@ export const Code: React.FC = ({ field }) => { const label = languages[language as keyof typeof languages] - const props: typeof field = useMemo( + const props: CodeFieldClient = useMemo( () => ({ ...field, + type: 'code', admin: { ...field.admin, - components: field.admin?.components || {}, - editorOptions: field.admin?.editorOptions || {}, + description: 'test', + editorOptions: { + onMount: (editor, monaco) => { + // Set module resolution to NodeNext to enable autocompletion + monaco.languages.typescript.typescriptDefaults.setCompilerOptions({ + allowNonTsExtensions: true, + module: monaco.languages.typescript.ModuleKind.ESNext, + moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs, + target: monaco.languages.typescript.ScriptTarget.ESNext, + typeRoots: ['node_modules/@types'], + }) + + monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({ + noSemanticValidation: false, + noSyntaxValidation: false, + }) + + const libUri = 'node_modules/@types/payload/index.d.ts' + + const run = async () => { + const types = await fetch('https://unpkg.com/payload@latest/dist/index.d.ts') + const libSource = await types.text() + monaco.languages.typescript.typescriptDefaults.addExtraLib(libSource, libUri) + } + void run() + }, + } as any, label, language: languageKeyToMonacoLanguageMap[language] || language, }, @@ -37,5 +73,42 @@ export const Code: React.FC = ({ field }) => { const key = `${field.name}-${language}-${label}` - return + return ( + props && ( + { + console.log('editor mounted') + // Set module resolution to NodeNext to enable autocompletion + monaco.languages.typescript.typescriptDefaults.setCompilerOptions({ + allowNonTsExtensions: true, + moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs, + paths: { + payload: ['file:///node_modules/payload/index.d.ts'], + }, + target: monaco.languages.typescript.ScriptTarget.ESNext, + typeRoots: ['node_modules/@types', 'node_modules/payload'], + }) + const run = async () => { + const types = await fetch('https://unpkg.com/payload@latest/dist/index.d.ts') + const typesText = await types.text() + monaco.languages.typescript.typescriptDefaults.addExtraLib( + typesText, + 'file:///node_modules/payload/index.d.ts', + ) + } + void run() + }} + path={path} + permissions={permissions} + readOnly={readOnly} + renderedBlocks={renderedBlocks} + schemaPath={schemaPath} + validate={validate} + /> + ) + ) }