PluginProbe
Code Snippets / 3.10.0-beta.1
Code Snippets v3.10.0-beta.1
4.0.0-beta.2 3.10.2 3.10.1 3.10.0 3.10.0-beta.2 3.10.0-beta.1 4.0.0-beta.1 3.9.6 trunk 2.10.0 2.10.1 2.12.0 2.12.1 2.13.0 2.13.1 2.13.2 2.13.3 2.14.0 2.14.1 2.14.2 2.14.3 2.14.4 2.14.5 2.14.6 3.0.0 All 65 releases
code-snippets / js / components / EditMenu / SnippetForm / fields / CodeEditor.tsx

CodeEditor.tsx in Code Snippets 3.10.0-beta.1, at js/components/EditMenu/SnippetForm/fields/CodeEditor.tsx

132 lines 4.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import React, { useEffect, useId, useRef } from 'react'
2 import { __ } from '@wordpress/i18n'
3 import { useSubmitSnippet } from '../../../../hooks/useSubmitSnippet'
4 import { handleUnknownError } from '../../../../utils/errors'
5 import { isMacOS } from '../../../../utils/screen'
6 import { useSnippetForm } from '../WithSnippetFormContext'
7 import { Button } from '../../../common/Button'
8 import { ExpandIcon } from '../../../common/icons/ExpandIcon'
9 import { MinimiseIcon } from '../../../common/icons/MinimiseIcon'
10 import { CodeEditorShortcuts } from './CodeEditorShortcuts'
11 import type { Dispatch, RefObject, SetStateAction } from 'react'
12
13 interface EditorTextareaProps {
14 textareaRef: RefObject<HTMLTextAreaElement>
15 }
16
17 const useFocusEditorShortcut = (
18 textareaRef: RefObject<HTMLTextAreaElement>
19 ) => {
20 const { codeEditorInstance } = useSnippetForm()
21
22 useEffect(() => {
23 const focusEditor = () => {
24 if (codeEditorInstance) {
25 codeEditorInstance.codemirror.focus()
26 return
27 }
28
29 textareaRef.current?.focus()
30 }
31
32 window.addEventListener('code_snippets_focus_editor', focusEditor)
33
34 return () => {
35 window.removeEventListener('code_snippets_focus_editor', focusEditor)
36 }
37 }, [codeEditorInstance, textareaRef])
38 }
39
40 const EditorTextarea: React.FC<EditorTextareaProps> = ({ textareaRef }) => {
41 const descriptionId = useId()
42 const { snippet, setSnippet } = useSnippetForm()
43
44 return (
45 <div
46 className="snippet-editor"
47 role="application"
48 aria-label={__('Code editor', 'code-snippets')}
49 aria-describedby={descriptionId}
50 >
51 <p id={descriptionId} className="screen-reader-text">
52 {__('In the editing area, the Tab key enters a tab character. To exit the code editor, press the Escape key and then the Tab key.', 'code-snippets')}
53 </p>
54 <textarea
55 ref={textareaRef}
56 id="snippet-code"
57 name="snippet_code"
58 value={snippet.code}
59 aria-label={__('Snippet code', 'code-snippets')}
60 rows={200}
61 spellCheck={false}
62 onChange={event => {
63 setSnippet(previous => ({ ...previous, code: event.target.value }))
64 }}
65 />
66 <CodeEditorShortcuts editorTheme={window.CODE_SNIPPETS_EDIT?.editorTheme ?? 'default'} />
67 </div>
68 )
69 }
70
71 export interface CodeEditorProps {
72 isExpanded: boolean
73 setIsExpanded: Dispatch<SetStateAction<boolean>>
74 }
75
76 export const CodeEditor: React.FC<CodeEditorProps> = ({ isExpanded, setIsExpanded }) => {
77 const { snippet, setSnippet, codeEditorInstance, setCodeEditorInstance } = useSnippetForm()
78 const { submitSnippet } = useSubmitSnippet()
79 const textareaRef = useRef<HTMLTextAreaElement>(null)
80
81 useEffect(() => {
82 setCodeEditorInstance(editorInstance => {
83 if (textareaRef.current && !editorInstance && window.wp.codeEditor) {
84 editorInstance = window.wp.codeEditor.initialize(textareaRef.current)
85
86 editorInstance.codemirror.on('changes', instance => {
87 setSnippet(previous => ({ ...previous, code: instance.getValue() }))
88 })
89 }
90
91 return editorInstance
92 })
93 }, [setCodeEditorInstance, textareaRef, setSnippet])
94
95 useEffect(() => {
96 if (codeEditorInstance) {
97 const extraKeys = codeEditorInstance.codemirror.getOption('extraKeys') ?? {}
98 const controlKey = isMacOS() ? 'Cmd' : 'Ctrl'
99 const onSave = () => {
100 submitSnippet(snippet)
101 .then(() => undefined)
102 .catch(handleUnknownError)
103 }
104
105 codeEditorInstance.codemirror.setOption('extraKeys', {
106 ...'object' === typeof extraKeys ? extraKeys : undefined,
107 [`${controlKey}-S`]: onSave,
108 [`${controlKey}-Enter`]: onSave
109 })
110 }
111 }, [submitSnippet, codeEditorInstance, snippet])
112
113 useFocusEditorShortcut(textareaRef)
114
115 return (
116 <div className="snippet-code-container">
117 <div className="above-snippet-code">
118 <label htmlFor="snippet-code">
119 {__('Snippet Content', 'code-snippets')}
120 </label>
121
122 <Button small className="expand-editor-button" onClick={() => setIsExpanded(current => !current)}>
123 {isExpanded ? <MinimiseIcon aria-hidden="true" /> : <ExpandIcon aria-hidden="true" />}
124 {isExpanded ? __('Minimize', 'code-snippets') : __('Expand', 'code-snippets')}
125 </Button>
126 </div>
127
128 <EditorTextarea textareaRef={textareaRef} />
129 </div>
130 )
131 }
132