@sanity/code-input 6.0.4 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -16
- package/assets/all-options.png +0 -0
- package/assets/basic-input.png +0 -0
- package/dist/_chunks-es/CodeMirrorProxy.js +510 -0
- package/dist/_chunks-es/CodeMirrorProxy.js.map +1 -0
- package/dist/_chunks-es/index.js +427 -0
- package/dist/_chunks-es/index.js.map +1 -0
- package/dist/index.d.ts +95 -0
- package/dist/index.d.ts.map +1 -0
- package/package.json +31 -86
- package/lib/_chunks-cjs/CodeMirrorProxy.cjs +0 -407
- package/lib/_chunks-cjs/CodeMirrorProxy.cjs.map +0 -1
- package/lib/_chunks-cjs/index.cjs +0 -433
- package/lib/_chunks-cjs/index.cjs.map +0 -1
- package/lib/_chunks-es/CodeMirrorProxy.js +0 -395
- package/lib/_chunks-es/CodeMirrorProxy.js.map +0 -1
- package/lib/_chunks-es/index.js +0 -437
- package/lib/_chunks-es/index.js.map +0 -1
- package/lib/index.cjs +0 -8
- package/lib/index.cjs.map +0 -1
- package/lib/index.d.cts +0 -121
- package/lib/index.d.ts +0 -121
- package/sanity.json +0 -8
- package/src/CodeInput.tsx +0 -181
- package/src/LanguageField.tsx +0 -41
- package/src/LanguageInput.tsx +0 -35
- package/src/PreviewCode.tsx +0 -71
- package/src/codemirror/CodeMirrorProxy.tsx +0 -159
- package/src/codemirror/CodeModeContext.tsx +0 -5
- package/src/codemirror/defaultCodeModes.ts +0 -109
- package/src/codemirror/extensions/backwardsCompatibleTone.ts +0 -19
- package/src/codemirror/extensions/highlightLineExtension.ts +0 -175
- package/src/codemirror/extensions/theme.ts +0 -65
- package/src/codemirror/extensions/useCodeMirrorTheme.ts +0 -64
- package/src/codemirror/extensions/useFontSize.ts +0 -24
- package/src/codemirror/useCodeMirror-client.test.tsx +0 -51
- package/src/codemirror/useCodeMirror-server.test.tsx +0 -18
- package/src/codemirror/useCodeMirror.tsx +0 -11
- package/src/codemirror/useLanguageMode.tsx +0 -60
- package/src/config.ts +0 -35
- package/src/getMedia.tsx +0 -93
- package/src/index.ts +0 -8
- package/src/plugin.tsx +0 -40
- package/src/sanity-ui.d.ts +0 -5
- package/src/schema.tsx +0 -86
- package/src/types.ts +0 -35
- package/src/ui/focusRingStyle.ts +0 -27
- package/src/useFieldMember.ts +0 -16
- package/v2-incompatible.js +0 -11
- /package/{lib → dist}/index.js +0 -0
- /package/{lib → dist}/index.js.map +0 -0
package/lib/index.d.ts
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import type {Extension} from '@codemirror/state'
|
|
2
|
-
import {JSX} from 'react'
|
|
3
|
-
import {ObjectDefinition} from 'sanity'
|
|
4
|
-
import {ObjectInputProps} from 'sanity'
|
|
5
|
-
import type {ObjectSchemaType} from 'sanity'
|
|
6
|
-
import {Plugin as Plugin_2} from 'sanity'
|
|
7
|
-
import {PreviewConfig} from 'sanity'
|
|
8
|
-
import type {PreviewProps} from 'sanity'
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* @public
|
|
12
|
-
*/
|
|
13
|
-
export declare interface CodeDefinition
|
|
14
|
-
extends Omit<ObjectDefinition, 'type' | 'fields' | 'options'> {
|
|
15
|
-
type: typeof codeTypeName
|
|
16
|
-
options?: CodeOptions
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/** @public */
|
|
20
|
-
export declare function CodeInput(props: CodeInputProps): React.JSX.Element
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* @public
|
|
24
|
-
*/
|
|
25
|
-
export declare const codeInput: Plugin_2<void | CodeInputConfig>
|
|
26
|
-
|
|
27
|
-
declare interface CodeInputConfig {
|
|
28
|
-
codeModes?: CodeMode[]
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export declare interface CodeInputLanguage {
|
|
32
|
-
title: string
|
|
33
|
-
value: string
|
|
34
|
-
mode?: string
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* @public
|
|
39
|
-
*/
|
|
40
|
-
export declare interface CodeInputProps extends ObjectInputProps<CodeInputValue, CodeSchemaType> {}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* @public
|
|
44
|
-
*/
|
|
45
|
-
export declare interface CodeInputValue {
|
|
46
|
-
_type?: 'code'
|
|
47
|
-
code?: string
|
|
48
|
-
filename?: string
|
|
49
|
-
language?: string
|
|
50
|
-
highlightedLines?: number[]
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
declare interface CodeMode {
|
|
54
|
-
name: string
|
|
55
|
-
loader: ModeLoader
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* @public
|
|
60
|
-
*/
|
|
61
|
-
export declare interface CodeOptions {
|
|
62
|
-
theme?: string
|
|
63
|
-
darkTheme?: string
|
|
64
|
-
languageAlternatives?: CodeInputLanguage[]
|
|
65
|
-
language?: string
|
|
66
|
-
withFilename?: boolean
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* @public
|
|
71
|
-
*/
|
|
72
|
-
export declare const codeSchema: {
|
|
73
|
-
type: 'object'
|
|
74
|
-
name: 'code'
|
|
75
|
-
} & Omit<ObjectDefinition, 'preview'> & {
|
|
76
|
-
preview?:
|
|
77
|
-
| PreviewConfig<
|
|
78
|
-
{
|
|
79
|
-
language: string
|
|
80
|
-
code: string
|
|
81
|
-
filename: string
|
|
82
|
-
highlightedLines: string
|
|
83
|
-
},
|
|
84
|
-
Record<'language' | 'code' | 'filename' | 'highlightedLines', any>
|
|
85
|
-
>
|
|
86
|
-
| undefined
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* @public
|
|
91
|
-
*/
|
|
92
|
-
export declare interface CodeSchemaType extends Omit<ObjectSchemaType, 'options'> {
|
|
93
|
-
options?: CodeOptions
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* @public
|
|
98
|
-
*/
|
|
99
|
-
export declare const codeTypeName: 'code'
|
|
100
|
-
|
|
101
|
-
declare type ModeLoader = () => Promise<Extension | undefined> | Extension | undefined
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* @public
|
|
105
|
-
*/
|
|
106
|
-
export declare function PreviewCode(props: PreviewCodeProps): JSX.Element
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* @public
|
|
110
|
-
*/
|
|
111
|
-
export declare interface PreviewCodeProps extends PreviewProps {
|
|
112
|
-
selection?: CodeInputValue
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export {}
|
|
116
|
-
|
|
117
|
-
declare module 'sanity' {
|
|
118
|
-
interface IntrinsicDefinitions {
|
|
119
|
-
code: CodeDefinition
|
|
120
|
-
}
|
|
121
|
-
}
|
package/sanity.json
DELETED
package/src/CodeInput.tsx
DELETED
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
import {Box, Card, Stack, Text} from '@sanity/ui'
|
|
2
|
-
import {Suspense, useCallback} from 'react'
|
|
3
|
-
import {
|
|
4
|
-
MemberField,
|
|
5
|
-
type ObjectInputProps,
|
|
6
|
-
type RenderInputCallback,
|
|
7
|
-
set,
|
|
8
|
-
setIfMissing,
|
|
9
|
-
unset,
|
|
10
|
-
} from 'sanity'
|
|
11
|
-
import {css, styled} from 'styled-components'
|
|
12
|
-
|
|
13
|
-
import {CodeMirrorProxy, useMounted} from './codemirror/useCodeMirror'
|
|
14
|
-
import {useLanguageMode} from './codemirror/useLanguageMode'
|
|
15
|
-
import {PATH_CODE} from './config'
|
|
16
|
-
import {LanguageField} from './LanguageField'
|
|
17
|
-
import type {CodeInputValue, CodeSchemaType} from './types'
|
|
18
|
-
import {focusRingBorderStyle, focusRingStyle} from './ui/focusRingStyle'
|
|
19
|
-
import {useFieldMember} from './useFieldMember'
|
|
20
|
-
|
|
21
|
-
export type {CodeInputLanguage, CodeInputValue} from './types'
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* @public
|
|
25
|
-
*/
|
|
26
|
-
export interface CodeInputProps extends ObjectInputProps<CodeInputValue, CodeSchemaType> {}
|
|
27
|
-
|
|
28
|
-
const EditorContainer = styled(Card)(({theme}) => {
|
|
29
|
-
const {focusRing, input} = theme.sanity
|
|
30
|
-
const base = theme.sanity.color.base
|
|
31
|
-
const color = theme.sanity.color.input
|
|
32
|
-
const border = {
|
|
33
|
-
color: color.default.enabled.border,
|
|
34
|
-
width: input.border.width,
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return css`
|
|
38
|
-
--input-box-shadow: ${focusRingBorderStyle(border)};
|
|
39
|
-
|
|
40
|
-
box-shadow: var(--input-box-shadow);
|
|
41
|
-
height: 250px;
|
|
42
|
-
min-height: 80px;
|
|
43
|
-
overflow-y: auto;
|
|
44
|
-
position: relative;
|
|
45
|
-
resize: vertical;
|
|
46
|
-
z-index: 0;
|
|
47
|
-
|
|
48
|
-
& > .cm-theme {
|
|
49
|
-
height: 100%;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
&:focus-within {
|
|
53
|
-
--input-box-shadow: ${focusRingStyle({
|
|
54
|
-
base,
|
|
55
|
-
border,
|
|
56
|
-
focusRing,
|
|
57
|
-
})};
|
|
58
|
-
}
|
|
59
|
-
`
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
/** @public */
|
|
63
|
-
export function CodeInput(props: CodeInputProps): React.JSX.Element {
|
|
64
|
-
const {
|
|
65
|
-
members,
|
|
66
|
-
elementProps,
|
|
67
|
-
onChange,
|
|
68
|
-
readOnly,
|
|
69
|
-
renderField,
|
|
70
|
-
renderInput,
|
|
71
|
-
renderItem,
|
|
72
|
-
renderPreview,
|
|
73
|
-
schemaType: type,
|
|
74
|
-
value,
|
|
75
|
-
onPathFocus,
|
|
76
|
-
} = props
|
|
77
|
-
|
|
78
|
-
const languageFieldMember = useFieldMember(members, 'language')
|
|
79
|
-
const filenameMember = useFieldMember(members, 'filename')
|
|
80
|
-
const codeFieldMember = useFieldMember(members, 'code')
|
|
81
|
-
|
|
82
|
-
const handleCodeFocus = useCallback(() => {
|
|
83
|
-
onPathFocus(PATH_CODE)
|
|
84
|
-
}, [onPathFocus])
|
|
85
|
-
|
|
86
|
-
const onHighlightChange = useCallback(
|
|
87
|
-
(lines: number[]) => onChange(set(lines, ['highlightedLines'])),
|
|
88
|
-
[onChange],
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
const handleCodeChange = useCallback(
|
|
92
|
-
(code: string) => {
|
|
93
|
-
const path = PATH_CODE
|
|
94
|
-
const fixedLanguage = type.options?.language
|
|
95
|
-
|
|
96
|
-
onChange([
|
|
97
|
-
setIfMissing({_type: type.name, language: fixedLanguage}),
|
|
98
|
-
code ? set(code, path) : unset(path),
|
|
99
|
-
])
|
|
100
|
-
},
|
|
101
|
-
[onChange, type],
|
|
102
|
-
)
|
|
103
|
-
const {languages, language, languageMode} = useLanguageMode(props.schemaType, props.value)
|
|
104
|
-
|
|
105
|
-
const mounted = useMounted()
|
|
106
|
-
|
|
107
|
-
const renderCodeInput: RenderInputCallback = useCallback(
|
|
108
|
-
(inputProps) => {
|
|
109
|
-
return (
|
|
110
|
-
<EditorContainer border overflow="hidden" radius={1} sizing="border" readOnly={readOnly}>
|
|
111
|
-
{mounted && (
|
|
112
|
-
<Suspense
|
|
113
|
-
fallback={
|
|
114
|
-
<Box padding={3}>
|
|
115
|
-
<Text>Loading code editor...</Text>
|
|
116
|
-
</Box>
|
|
117
|
-
}
|
|
118
|
-
>
|
|
119
|
-
<CodeMirrorProxy
|
|
120
|
-
languageMode={languageMode}
|
|
121
|
-
onChange={handleCodeChange}
|
|
122
|
-
value={inputProps.value as string}
|
|
123
|
-
highlightLines={value?.highlightedLines}
|
|
124
|
-
onHighlightChange={onHighlightChange}
|
|
125
|
-
readOnly={readOnly}
|
|
126
|
-
onFocus={handleCodeFocus}
|
|
127
|
-
onBlur={elementProps.onBlur}
|
|
128
|
-
/>
|
|
129
|
-
</Suspense>
|
|
130
|
-
)}
|
|
131
|
-
</EditorContainer>
|
|
132
|
-
)
|
|
133
|
-
},
|
|
134
|
-
[
|
|
135
|
-
readOnly,
|
|
136
|
-
mounted,
|
|
137
|
-
languageMode,
|
|
138
|
-
handleCodeChange,
|
|
139
|
-
value?.highlightedLines,
|
|
140
|
-
onHighlightChange,
|
|
141
|
-
handleCodeFocus,
|
|
142
|
-
elementProps.onBlur,
|
|
143
|
-
],
|
|
144
|
-
)
|
|
145
|
-
|
|
146
|
-
return (
|
|
147
|
-
<Stack space={4}>
|
|
148
|
-
{languageFieldMember && (
|
|
149
|
-
<LanguageField
|
|
150
|
-
member={languageFieldMember}
|
|
151
|
-
language={language}
|
|
152
|
-
languages={languages}
|
|
153
|
-
renderField={renderField}
|
|
154
|
-
renderItem={renderItem}
|
|
155
|
-
renderInput={renderInput}
|
|
156
|
-
renderPreview={renderPreview}
|
|
157
|
-
/>
|
|
158
|
-
)}
|
|
159
|
-
|
|
160
|
-
{type.options?.withFilename && filenameMember && (
|
|
161
|
-
<MemberField
|
|
162
|
-
member={filenameMember}
|
|
163
|
-
renderItem={renderItem}
|
|
164
|
-
renderField={renderField}
|
|
165
|
-
renderInput={renderInput}
|
|
166
|
-
renderPreview={renderPreview}
|
|
167
|
-
/>
|
|
168
|
-
)}
|
|
169
|
-
|
|
170
|
-
{codeFieldMember && (
|
|
171
|
-
<MemberField
|
|
172
|
-
member={codeFieldMember}
|
|
173
|
-
renderInput={renderCodeInput}
|
|
174
|
-
renderItem={renderItem}
|
|
175
|
-
renderField={renderField}
|
|
176
|
-
renderPreview={renderPreview}
|
|
177
|
-
/>
|
|
178
|
-
)}
|
|
179
|
-
</Stack>
|
|
180
|
-
)
|
|
181
|
-
}
|
package/src/LanguageField.tsx
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import {useCallback} from 'react'
|
|
2
|
-
import {
|
|
3
|
-
type FieldMember,
|
|
4
|
-
type InputProps,
|
|
5
|
-
MemberField,
|
|
6
|
-
type MemberFieldProps,
|
|
7
|
-
type PrimitiveInputElementProps,
|
|
8
|
-
} from 'sanity'
|
|
9
|
-
|
|
10
|
-
import {LanguageInput} from './LanguageInput'
|
|
11
|
-
import type {CodeInputLanguage} from './types'
|
|
12
|
-
|
|
13
|
-
export function LanguageField(
|
|
14
|
-
props: MemberFieldProps & {member: FieldMember; language: string; languages: CodeInputLanguage[]},
|
|
15
|
-
) {
|
|
16
|
-
const {member, languages, language, renderItem, renderField, renderPreview} = props
|
|
17
|
-
|
|
18
|
-
const renderInput = useCallback(
|
|
19
|
-
({elementProps, onChange}: Omit<InputProps, 'renderDefault'>) => {
|
|
20
|
-
return (
|
|
21
|
-
<LanguageInput
|
|
22
|
-
onChange={onChange}
|
|
23
|
-
elementProps={elementProps as PrimitiveInputElementProps}
|
|
24
|
-
language={language}
|
|
25
|
-
languages={languages}
|
|
26
|
-
/>
|
|
27
|
-
)
|
|
28
|
-
},
|
|
29
|
-
[languages, language],
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
return (
|
|
33
|
-
<MemberField
|
|
34
|
-
member={member}
|
|
35
|
-
renderItem={renderItem}
|
|
36
|
-
renderField={renderField}
|
|
37
|
-
renderInput={renderInput}
|
|
38
|
-
renderPreview={renderPreview}
|
|
39
|
-
/>
|
|
40
|
-
)
|
|
41
|
-
}
|
package/src/LanguageInput.tsx
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import {Select} from '@sanity/ui'
|
|
2
|
-
import {type ChangeEvent, useCallback} from 'react'
|
|
3
|
-
import {set, type StringInputProps, unset} from 'sanity'
|
|
4
|
-
|
|
5
|
-
import type {CodeInputLanguage} from './types'
|
|
6
|
-
|
|
7
|
-
export interface LanguageInputProps {
|
|
8
|
-
language: string
|
|
9
|
-
languages: CodeInputLanguage[]
|
|
10
|
-
onChange: StringInputProps['onChange']
|
|
11
|
-
elementProps: StringInputProps['elementProps']
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/** @internal */
|
|
15
|
-
export function LanguageInput(props: LanguageInputProps) {
|
|
16
|
-
const {language, languages, onChange, elementProps} = props
|
|
17
|
-
|
|
18
|
-
const handleChange = useCallback(
|
|
19
|
-
(e: ChangeEvent<HTMLSelectElement>) => {
|
|
20
|
-
const newValue = e.currentTarget.value
|
|
21
|
-
onChange(newValue ? set(newValue) : unset())
|
|
22
|
-
},
|
|
23
|
-
[onChange],
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
return (
|
|
27
|
-
<Select {...elementProps} value={language} onChange={handleChange}>
|
|
28
|
-
{languages.map((lang: {title: string; value: string}) => (
|
|
29
|
-
<option key={lang.value} value={lang.value}>
|
|
30
|
-
{lang.title}
|
|
31
|
-
</option>
|
|
32
|
-
))}
|
|
33
|
-
</Select>
|
|
34
|
-
)
|
|
35
|
-
}
|
package/src/PreviewCode.tsx
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import {Box, Card, Flex, Label, Text} from '@sanity/ui'
|
|
2
|
-
import {Suspense} from 'react'
|
|
3
|
-
import type {PreviewProps} from 'sanity'
|
|
4
|
-
import {styled} from 'styled-components'
|
|
5
|
-
|
|
6
|
-
import {CodeMirrorProxy, useMounted} from './codemirror/useCodeMirror'
|
|
7
|
-
import {useLanguageMode} from './codemirror/useLanguageMode'
|
|
8
|
-
import type {CodeInputValue, CodeSchemaType} from './types'
|
|
9
|
-
|
|
10
|
-
const PreviewContainer = styled(Box)`
|
|
11
|
-
position: relative;
|
|
12
|
-
`
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* @public
|
|
16
|
-
*/
|
|
17
|
-
export interface PreviewCodeProps extends PreviewProps {
|
|
18
|
-
selection?: CodeInputValue
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* @public
|
|
23
|
-
*/
|
|
24
|
-
export function PreviewCode(props: PreviewCodeProps) {
|
|
25
|
-
const {selection, schemaType: type} = props
|
|
26
|
-
const {languageMode} = useLanguageMode(type as CodeSchemaType, props.selection)
|
|
27
|
-
|
|
28
|
-
const mounted = useMounted()
|
|
29
|
-
return (
|
|
30
|
-
<PreviewContainer>
|
|
31
|
-
<Card padding={4}>
|
|
32
|
-
{selection?.filename || selection?.language ? (
|
|
33
|
-
<Card
|
|
34
|
-
paddingBottom={4}
|
|
35
|
-
marginBottom={selection.code ? 4 : 0}
|
|
36
|
-
borderBottom={!!selection.code}
|
|
37
|
-
>
|
|
38
|
-
<Flex align="center" justify="flex-end">
|
|
39
|
-
{selection?.filename ? (
|
|
40
|
-
<Box flex={1}>
|
|
41
|
-
<Text>
|
|
42
|
-
<code>{selection.filename}</code>
|
|
43
|
-
</Text>
|
|
44
|
-
</Box>
|
|
45
|
-
) : null}
|
|
46
|
-
{selection?.language ? <Label muted>{selection.language}</Label> : null}
|
|
47
|
-
</Flex>
|
|
48
|
-
</Card>
|
|
49
|
-
) : null}
|
|
50
|
-
{mounted && (
|
|
51
|
-
<Suspense fallback={<Card padding={2}>Loading code preview...</Card>}>
|
|
52
|
-
<CodeMirrorProxy
|
|
53
|
-
readOnly
|
|
54
|
-
editable={false}
|
|
55
|
-
value={selection?.code || ''}
|
|
56
|
-
highlightLines={selection?.highlightedLines || []}
|
|
57
|
-
basicSetup={{
|
|
58
|
-
lineNumbers: false,
|
|
59
|
-
foldGutter: false,
|
|
60
|
-
highlightSelectionMatches: false,
|
|
61
|
-
highlightActiveLineGutter: false,
|
|
62
|
-
highlightActiveLine: false,
|
|
63
|
-
}}
|
|
64
|
-
languageMode={languageMode}
|
|
65
|
-
/>
|
|
66
|
-
</Suspense>
|
|
67
|
-
)}
|
|
68
|
-
</Card>
|
|
69
|
-
</PreviewContainer>
|
|
70
|
-
)
|
|
71
|
-
}
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import type {Extension} from '@codemirror/state'
|
|
2
|
-
import {EditorView} from '@codemirror/view'
|
|
3
|
-
import {useRootTheme} from '@sanity/ui'
|
|
4
|
-
import CodeMirror, {type ReactCodeMirrorProps, type ReactCodeMirrorRef} from '@uiw/react-codemirror'
|
|
5
|
-
import {forwardRef, useCallback, useContext, useEffect, useMemo, useState} from 'react'
|
|
6
|
-
|
|
7
|
-
import {CodeInputConfigContext} from './CodeModeContext'
|
|
8
|
-
import {defaultCodeModes} from './defaultCodeModes'
|
|
9
|
-
import {
|
|
10
|
-
highlightLine,
|
|
11
|
-
highlightState,
|
|
12
|
-
setHighlightedLines,
|
|
13
|
-
} from './extensions/highlightLineExtension'
|
|
14
|
-
import {useThemeExtension} from './extensions/theme'
|
|
15
|
-
import {useCodeMirrorTheme} from './extensions/useCodeMirrorTheme'
|
|
16
|
-
import {useFontSizeExtension} from './extensions/useFontSize'
|
|
17
|
-
|
|
18
|
-
export interface CodeMirrorProps extends ReactCodeMirrorProps {
|
|
19
|
-
highlightLines?: number[]
|
|
20
|
-
languageMode?: string
|
|
21
|
-
onHighlightChange?: (lines: number[]) => void
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* CodeMirrorProxy is a wrapper component around CodeMirror that we lazy load to reduce initial bundle size.
|
|
26
|
-
*
|
|
27
|
-
* It is also responsible for integrating any CodeMirror extensions.
|
|
28
|
-
*/
|
|
29
|
-
const CodeMirrorProxy = forwardRef<ReactCodeMirrorRef, CodeMirrorProps>(
|
|
30
|
-
function CodeMirrorProxy(props, ref) {
|
|
31
|
-
const {
|
|
32
|
-
basicSetup: basicSetupProp,
|
|
33
|
-
highlightLines,
|
|
34
|
-
languageMode,
|
|
35
|
-
onHighlightChange,
|
|
36
|
-
readOnly,
|
|
37
|
-
value,
|
|
38
|
-
...codeMirrorProps
|
|
39
|
-
} = props
|
|
40
|
-
|
|
41
|
-
const themeCtx = useRootTheme()
|
|
42
|
-
const codeMirrorTheme = useCodeMirrorTheme()
|
|
43
|
-
const [editorView, setEditorView] = useState<EditorView | undefined>(undefined)
|
|
44
|
-
|
|
45
|
-
// Resolve extensions
|
|
46
|
-
const themeExtension = useThemeExtension()
|
|
47
|
-
const fontSizeExtension = useFontSizeExtension({fontSize: 1})
|
|
48
|
-
const languageExtension = useLanguageExtension(languageMode)
|
|
49
|
-
const highlightLineExtension = useMemo(
|
|
50
|
-
() =>
|
|
51
|
-
highlightLine({
|
|
52
|
-
onHighlightChange,
|
|
53
|
-
readOnly,
|
|
54
|
-
theme: themeCtx,
|
|
55
|
-
}),
|
|
56
|
-
[onHighlightChange, readOnly, themeCtx],
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
const extensions = useMemo(() => {
|
|
60
|
-
const baseExtensions = [
|
|
61
|
-
themeExtension,
|
|
62
|
-
fontSizeExtension,
|
|
63
|
-
highlightLineExtension,
|
|
64
|
-
EditorView.lineWrapping,
|
|
65
|
-
]
|
|
66
|
-
if (languageExtension) {
|
|
67
|
-
return [...baseExtensions, languageExtension]
|
|
68
|
-
}
|
|
69
|
-
return baseExtensions
|
|
70
|
-
}, [fontSizeExtension, highlightLineExtension, languageExtension, themeExtension])
|
|
71
|
-
|
|
72
|
-
useEffect(() => {
|
|
73
|
-
if (editorView) {
|
|
74
|
-
setHighlightedLines(editorView, highlightLines ?? [])
|
|
75
|
-
}
|
|
76
|
-
}, [editorView, highlightLines, value])
|
|
77
|
-
|
|
78
|
-
const initialState = useMemo(() => {
|
|
79
|
-
return {
|
|
80
|
-
json: {
|
|
81
|
-
doc: value ?? '',
|
|
82
|
-
selection: {
|
|
83
|
-
main: 0,
|
|
84
|
-
ranges: [{anchor: 0, head: 0}],
|
|
85
|
-
},
|
|
86
|
-
highlight: highlightLines ?? [],
|
|
87
|
-
},
|
|
88
|
-
fields: highlightState,
|
|
89
|
-
}
|
|
90
|
-
// only need to calculate this on initial render
|
|
91
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
92
|
-
}, [])
|
|
93
|
-
|
|
94
|
-
const handleCreateEditor = useCallback((view: EditorView) => {
|
|
95
|
-
setEditorView(view)
|
|
96
|
-
}, [])
|
|
97
|
-
|
|
98
|
-
const basicSetup = useMemo(
|
|
99
|
-
() =>
|
|
100
|
-
basicSetupProp ?? {
|
|
101
|
-
highlightActiveLine: false,
|
|
102
|
-
},
|
|
103
|
-
[basicSetupProp],
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
return (
|
|
107
|
-
<CodeMirror
|
|
108
|
-
{...codeMirrorProps}
|
|
109
|
-
value={value}
|
|
110
|
-
ref={ref}
|
|
111
|
-
extensions={extensions}
|
|
112
|
-
theme={codeMirrorTheme}
|
|
113
|
-
onCreateEditor={handleCreateEditor}
|
|
114
|
-
initialState={initialState}
|
|
115
|
-
basicSetup={basicSetup}
|
|
116
|
-
/>
|
|
117
|
-
)
|
|
118
|
-
},
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
function useLanguageExtension(mode?: string) {
|
|
122
|
-
const codeConfig = useContext(CodeInputConfigContext)
|
|
123
|
-
|
|
124
|
-
const [languageExtension, setLanguageExtension] = useState<Extension | undefined>()
|
|
125
|
-
|
|
126
|
-
useEffect(() => {
|
|
127
|
-
const customModes = codeConfig?.codeModes ?? []
|
|
128
|
-
const modes = [...customModes, ...defaultCodeModes]
|
|
129
|
-
|
|
130
|
-
const codeMode = modes.find((m) => m.name === mode)
|
|
131
|
-
if (!codeMode?.loader) {
|
|
132
|
-
// eslint-disable-next-line no-console
|
|
133
|
-
console.warn(
|
|
134
|
-
`Found no codeMode for language mode ${mode}, syntax highlighting will be disabled.`,
|
|
135
|
-
)
|
|
136
|
-
}
|
|
137
|
-
let active = true
|
|
138
|
-
Promise.resolve(codeMode?.loader())
|
|
139
|
-
.then((extension) => {
|
|
140
|
-
if (active) {
|
|
141
|
-
setLanguageExtension(extension)
|
|
142
|
-
}
|
|
143
|
-
})
|
|
144
|
-
.catch((e) => {
|
|
145
|
-
// eslint-disable-next-line no-console
|
|
146
|
-
console.error(`Failed to load language mode ${mode}`, e)
|
|
147
|
-
if (active) {
|
|
148
|
-
setLanguageExtension(undefined)
|
|
149
|
-
}
|
|
150
|
-
})
|
|
151
|
-
return () => {
|
|
152
|
-
active = false
|
|
153
|
-
}
|
|
154
|
-
}, [mode, codeConfig])
|
|
155
|
-
|
|
156
|
-
return languageExtension
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export default CodeMirrorProxy
|