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
<CodeField
        autoComplete={autoComplete}
        field={props}
        forceRender={forceRender}
        key={key}
        onMount={(editor, monaco) => {
          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}
      />
```
This commit is contained in:
Alessio Gravili
2024-12-28 12:09:51 -07:00
committed by GitHub
parent fabe5411f4
commit 68a5f5ca2f
3 changed files with 83 additions and 6 deletions

View File

@@ -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<CodeFieldClient, 'type'>
type CodeFieldBaseClientProps = {
readonly autoComplete?: string
readonly onMount?: EditorProps['onMount']
readonly path: string
readonly validate?: CodeFieldValidation
}

View File

@@ -30,6 +30,7 @@ const CodeFieldComponent: CodeFieldClientComponent = (props) => {
localized,
required,
},
onMount,
path,
readOnly,
validate,
@@ -84,6 +85,7 @@ const CodeFieldComponent: CodeFieldClientComponent = (props) => {
<CodeEditor
defaultLanguage={prismToMonacoLanguageMap[language] || language}
onChange={readOnly ? () => null : (val) => setValue(val)}
onMount={onMount}
options={editorOptions}
readOnly={readOnly}
value={(value as string) || ''}

View File

@@ -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<CodeFieldClientProps> = ({ field }) => {
export const Code: React.FC<CodeFieldClientProps> = ({
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<CodeFieldClientProps> = ({ field }) => {
const label = languages[language as keyof typeof languages]
const props: typeof field = useMemo(
const props: CodeFieldClient = useMemo<CodeFieldClient>(
() => ({
...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<CodeFieldClientProps> = ({ field }) => {
const key = `${field.name}-${language}-${label}`
return <CodeField field={props} key={key} />
return (
props && (
<CodeField
autoComplete={autoComplete}
field={props}
forceRender={forceRender}
key={key}
onMount={(editor, monaco) => {
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}
/>
)
)
}