@sanity/code-input 6.0.3 → 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
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import {StreamLanguage} from '@codemirror/language'
|
|
2
|
-
import type {Extension} from '@codemirror/state'
|
|
3
|
-
|
|
4
|
-
export interface CodeMode {
|
|
5
|
-
name: string
|
|
6
|
-
loader: ModeLoader
|
|
7
|
-
}
|
|
8
|
-
export type ModeLoader = () => Promise<Extension | undefined> | Extension | undefined
|
|
9
|
-
|
|
10
|
-
export const defaultCodeModes: CodeMode[] = [
|
|
11
|
-
{
|
|
12
|
-
name: 'groq',
|
|
13
|
-
loader: () =>
|
|
14
|
-
import('@codemirror/lang-javascript').then(({javascriptLanguage}) => javascriptLanguage),
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
name: 'javascript',
|
|
18
|
-
loader: () =>
|
|
19
|
-
import('@codemirror/lang-javascript').then(({javascript}) => javascript({jsx: false})),
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
name: 'jsx',
|
|
23
|
-
loader: () =>
|
|
24
|
-
import('@codemirror/lang-javascript').then(({javascript}) => javascript({jsx: true})),
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
name: 'typescript',
|
|
28
|
-
loader: () =>
|
|
29
|
-
import('@codemirror/lang-javascript').then(({javascript}) =>
|
|
30
|
-
javascript({jsx: false, typescript: true}),
|
|
31
|
-
),
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
name: 'tsx',
|
|
35
|
-
loader: () =>
|
|
36
|
-
import('@codemirror/lang-javascript').then(({javascript}) =>
|
|
37
|
-
javascript({jsx: true, typescript: true}),
|
|
38
|
-
),
|
|
39
|
-
},
|
|
40
|
-
{name: 'php', loader: () => import('@codemirror/lang-php').then(({php}) => php())},
|
|
41
|
-
{name: 'sql', loader: () => import('@codemirror/lang-sql').then(({sql}) => sql())},
|
|
42
|
-
{
|
|
43
|
-
name: 'mysql',
|
|
44
|
-
loader: () => import('@codemirror/lang-sql').then(({sql, MySQL}) => sql({dialect: MySQL})),
|
|
45
|
-
},
|
|
46
|
-
{name: 'json', loader: () => import('@codemirror/lang-json').then(({json}) => json())},
|
|
47
|
-
{
|
|
48
|
-
name: 'markdown',
|
|
49
|
-
loader: () => import('@codemirror/lang-markdown').then(({markdown}) => markdown()),
|
|
50
|
-
},
|
|
51
|
-
{name: 'java', loader: () => import('@codemirror/lang-java').then(({java}) => java())},
|
|
52
|
-
{name: 'html', loader: () => import('@codemirror/lang-html').then(({html}) => html())},
|
|
53
|
-
{
|
|
54
|
-
name: 'csharp',
|
|
55
|
-
loader: () =>
|
|
56
|
-
import('@codemirror/legacy-modes/mode/clike').then(({csharp}) =>
|
|
57
|
-
StreamLanguage.define(csharp),
|
|
58
|
-
),
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
name: 'sh',
|
|
62
|
-
loader: () =>
|
|
63
|
-
import('@codemirror/legacy-modes/mode/shell').then(({shell}) => StreamLanguage.define(shell)),
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
name: 'css',
|
|
67
|
-
loader: () =>
|
|
68
|
-
import('@codemirror/legacy-modes/mode/css').then(({css}) => StreamLanguage.define(css)),
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
name: 'scss',
|
|
72
|
-
loader: () =>
|
|
73
|
-
import('@codemirror/legacy-modes/mode/css').then(({css}) => StreamLanguage.define(css)),
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
name: 'sass',
|
|
77
|
-
loader: () =>
|
|
78
|
-
import('@codemirror/legacy-modes/mode/sass').then(({sass}) => StreamLanguage.define(sass)),
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
name: 'ruby',
|
|
82
|
-
loader: () =>
|
|
83
|
-
import('@codemirror/legacy-modes/mode/ruby').then(({ruby}) => StreamLanguage.define(ruby)),
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
name: 'python',
|
|
87
|
-
loader: () =>
|
|
88
|
-
import('@codemirror/legacy-modes/mode/python').then(({python}) =>
|
|
89
|
-
StreamLanguage.define(python),
|
|
90
|
-
),
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
name: 'xml',
|
|
94
|
-
loader: () =>
|
|
95
|
-
import('@codemirror/legacy-modes/mode/xml').then(({xml}) => StreamLanguage.define(xml)),
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
name: 'yaml',
|
|
99
|
-
loader: () =>
|
|
100
|
-
import('@codemirror/legacy-modes/mode/yaml').then(({yaml}) => StreamLanguage.define(yaml)),
|
|
101
|
-
},
|
|
102
|
-
{
|
|
103
|
-
name: 'golang',
|
|
104
|
-
loader: () =>
|
|
105
|
-
import('@codemirror/legacy-modes/mode/go').then(({go}) => StreamLanguage.define(go)),
|
|
106
|
-
},
|
|
107
|
-
{name: 'text', loader: () => undefined},
|
|
108
|
-
{name: 'batch', loader: () => undefined},
|
|
109
|
-
]
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type {ThemeContextValue} from '@sanity/ui'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* `@sanity/ui@v2.9` introduced two new tones; "neutral" and "suggest",
|
|
5
|
-
* which maps to "default" and "primary" respectively in the old theme.
|
|
6
|
-
* This function returns the "backwards compatible" tone value.
|
|
7
|
-
*
|
|
8
|
-
* @returns The tone value that is backwards compatible with the old theme.
|
|
9
|
-
* @internal
|
|
10
|
-
*/
|
|
11
|
-
export function getBackwardsCompatibleTone(
|
|
12
|
-
themeCtx: ThemeContextValue,
|
|
13
|
-
): Exclude<ThemeContextValue['tone'], 'neutral' | 'suggest'> {
|
|
14
|
-
if (themeCtx.tone !== 'neutral' && themeCtx.tone !== 'suggest') {
|
|
15
|
-
return themeCtx.tone
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
return themeCtx.tone === 'neutral' ? 'default' : 'primary'
|
|
19
|
-
}
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
import {type Extension, StateEffect, StateField} from '@codemirror/state'
|
|
2
|
-
import {Decoration, type DecorationSet, EditorView, lineNumbers} from '@codemirror/view'
|
|
3
|
-
import type {ThemeContextValue} from '@sanity/ui'
|
|
4
|
-
import {rgba} from '@sanity/ui/theme'
|
|
5
|
-
|
|
6
|
-
import {getBackwardsCompatibleTone} from './backwardsCompatibleTone'
|
|
7
|
-
|
|
8
|
-
const highlightLineClass = 'cm-highlight-line'
|
|
9
|
-
|
|
10
|
-
export const addLineHighlight = StateEffect.define<number>()
|
|
11
|
-
export const removeLineHighlight = StateEffect.define<number>()
|
|
12
|
-
|
|
13
|
-
export const lineHighlightField = StateField.define({
|
|
14
|
-
create() {
|
|
15
|
-
return Decoration.none
|
|
16
|
-
},
|
|
17
|
-
update(lines, tr) {
|
|
18
|
-
lines = lines.map(tr.changes)
|
|
19
|
-
for (const e of tr.effects) {
|
|
20
|
-
if (e.is(addLineHighlight)) {
|
|
21
|
-
lines = lines.update({add: [lineHighlightMark.range(e.value)]})
|
|
22
|
-
}
|
|
23
|
-
if (e.is(removeLineHighlight)) {
|
|
24
|
-
lines = lines.update({
|
|
25
|
-
filter: (from) => {
|
|
26
|
-
// removeLineHighlight value is lineStart for the highlight, so keep other effects
|
|
27
|
-
return from !== e.value
|
|
28
|
-
},
|
|
29
|
-
})
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return lines
|
|
33
|
-
},
|
|
34
|
-
toJSON(value, state) {
|
|
35
|
-
const highlightLines: number[] = []
|
|
36
|
-
const iter = value.iter()
|
|
37
|
-
while (iter.value) {
|
|
38
|
-
const lineNumber = state.doc.lineAt(iter.from).number
|
|
39
|
-
if (!highlightLines.includes(lineNumber)) {
|
|
40
|
-
highlightLines.push(lineNumber)
|
|
41
|
-
}
|
|
42
|
-
iter.next()
|
|
43
|
-
}
|
|
44
|
-
return highlightLines
|
|
45
|
-
},
|
|
46
|
-
fromJSON(value: number[], state) {
|
|
47
|
-
const lines = state.doc.lines
|
|
48
|
-
const highlights = value
|
|
49
|
-
.filter((line) => line <= lines) // one-indexed
|
|
50
|
-
.map((line) => lineHighlightMark.range(state.doc.line(line).from))
|
|
51
|
-
highlights.sort((a, b) => a.from - b.from)
|
|
52
|
-
try {
|
|
53
|
-
return Decoration.none.update({
|
|
54
|
-
add: highlights,
|
|
55
|
-
})
|
|
56
|
-
} catch (e) {
|
|
57
|
-
// eslint-disable-next-line no-console
|
|
58
|
-
console.error(e)
|
|
59
|
-
return Decoration.none
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
provide: (f) => EditorView.decorations.from(f),
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
const lineHighlightMark = Decoration.line({
|
|
66
|
-
class: highlightLineClass,
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
export const highlightState: {
|
|
70
|
-
[prop: string]: StateField<DecorationSet>
|
|
71
|
-
} = {
|
|
72
|
-
highlight: lineHighlightField,
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export interface HighlightLineConfig {
|
|
76
|
-
onHighlightChange?: (lines: number[]) => void
|
|
77
|
-
readOnly?: boolean
|
|
78
|
-
theme: ThemeContextValue
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function createCodeMirrorTheme(options: {themeCtx: ThemeContextValue}) {
|
|
82
|
-
const {themeCtx} = options
|
|
83
|
-
|
|
84
|
-
const fallbackTone = getBackwardsCompatibleTone(themeCtx)
|
|
85
|
-
|
|
86
|
-
const dark = {color: themeCtx.theme.color.dark[fallbackTone]}
|
|
87
|
-
const light = {color: themeCtx.theme.color.light[fallbackTone]}
|
|
88
|
-
|
|
89
|
-
return EditorView.baseTheme({
|
|
90
|
-
'.cm-lineNumbers': {
|
|
91
|
-
cursor: 'default',
|
|
92
|
-
},
|
|
93
|
-
'.cm-line.cm-line': {
|
|
94
|
-
position: 'relative',
|
|
95
|
-
},
|
|
96
|
-
|
|
97
|
-
// need set background with pseudoelement so it does not render over selection color
|
|
98
|
-
[`.${highlightLineClass}::before`]: {
|
|
99
|
-
position: 'absolute',
|
|
100
|
-
top: 0,
|
|
101
|
-
bottom: 0,
|
|
102
|
-
left: 0,
|
|
103
|
-
right: 0,
|
|
104
|
-
zIndex: -3,
|
|
105
|
-
content: "''",
|
|
106
|
-
boxSizing: 'border-box',
|
|
107
|
-
},
|
|
108
|
-
[`&dark .${highlightLineClass}::before`]: {
|
|
109
|
-
background: rgba(dark.color.muted.caution.pressed.bg, 0.5),
|
|
110
|
-
},
|
|
111
|
-
[`&light .${highlightLineClass}::before`]: {
|
|
112
|
-
background: rgba(light.color.muted.caution.pressed.bg, 0.75),
|
|
113
|
-
},
|
|
114
|
-
})
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export const highlightLine = (config: HighlightLineConfig): Extension => {
|
|
118
|
-
const highlightTheme = createCodeMirrorTheme({themeCtx: config.theme})
|
|
119
|
-
|
|
120
|
-
return [
|
|
121
|
-
lineHighlightField,
|
|
122
|
-
config.readOnly
|
|
123
|
-
? []
|
|
124
|
-
: lineNumbers({
|
|
125
|
-
domEventHandlers: {
|
|
126
|
-
mousedown: (editorView, lineInfo) => {
|
|
127
|
-
// Determine if the line for the clicked gutter line number has highlighted state or not
|
|
128
|
-
const line = editorView.state.doc.lineAt(lineInfo.from)
|
|
129
|
-
let isHighlighted = false
|
|
130
|
-
editorView.state
|
|
131
|
-
.field(lineHighlightField)
|
|
132
|
-
.between(line.from, line.to, (_from, _to, value) => {
|
|
133
|
-
if (value) {
|
|
134
|
-
isHighlighted = true
|
|
135
|
-
return false // stop iteration
|
|
136
|
-
}
|
|
137
|
-
return undefined
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
if (isHighlighted) {
|
|
141
|
-
editorView.dispatch({effects: removeLineHighlight.of(line.from)})
|
|
142
|
-
} else {
|
|
143
|
-
editorView.dispatch({effects: addLineHighlight.of(line.from)})
|
|
144
|
-
}
|
|
145
|
-
if (config?.onHighlightChange) {
|
|
146
|
-
config.onHighlightChange(editorView.state.toJSON(highlightState).highlight)
|
|
147
|
-
}
|
|
148
|
-
return true
|
|
149
|
-
},
|
|
150
|
-
},
|
|
151
|
-
}),
|
|
152
|
-
highlightTheme,
|
|
153
|
-
]
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Adds and removes highlights to the provided view using highlightLines
|
|
158
|
-
* @param view
|
|
159
|
-
* @param highlightLines
|
|
160
|
-
*/
|
|
161
|
-
export function setHighlightedLines(view: EditorView, highlightLines: number[]): void {
|
|
162
|
-
const doc = view.state.doc
|
|
163
|
-
const lines = doc.lines
|
|
164
|
-
//1-based line numbers
|
|
165
|
-
const allLineNumbers = Array.from({length: lines}, (_x, i) => i + 1)
|
|
166
|
-
view.dispatch({
|
|
167
|
-
effects: allLineNumbers.map((lineNumber) => {
|
|
168
|
-
const line = doc.line(lineNumber)
|
|
169
|
-
if (highlightLines?.includes(lineNumber)) {
|
|
170
|
-
return addLineHighlight.of(line.from)
|
|
171
|
-
}
|
|
172
|
-
return removeLineHighlight.of(line.from)
|
|
173
|
-
}),
|
|
174
|
-
})
|
|
175
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import type {Extension} from '@codemirror/state'
|
|
2
|
-
import {EditorView} from '@codemirror/view'
|
|
3
|
-
import {useRootTheme} from '@sanity/ui'
|
|
4
|
-
import {rgba} from '@sanity/ui/theme'
|
|
5
|
-
import {useMemo} from 'react'
|
|
6
|
-
|
|
7
|
-
import {getBackwardsCompatibleTone} from './backwardsCompatibleTone'
|
|
8
|
-
|
|
9
|
-
export function useThemeExtension(): Extension {
|
|
10
|
-
const themeCtx = useRootTheme()
|
|
11
|
-
|
|
12
|
-
return useMemo(() => {
|
|
13
|
-
const fallbackTone = getBackwardsCompatibleTone(themeCtx)
|
|
14
|
-
const dark = {color: themeCtx.theme.color.dark[fallbackTone]}
|
|
15
|
-
const light = {color: themeCtx.theme.color.light[fallbackTone]}
|
|
16
|
-
|
|
17
|
-
return EditorView.baseTheme({
|
|
18
|
-
'&.cm-editor': {
|
|
19
|
-
height: '100%',
|
|
20
|
-
},
|
|
21
|
-
'&.cm-editor.cm-focused': {
|
|
22
|
-
outline: 'none',
|
|
23
|
-
},
|
|
24
|
-
|
|
25
|
-
// Matching brackets
|
|
26
|
-
'&.cm-editor.cm-focused .cm-matchingBracket': {
|
|
27
|
-
backgroundColor: 'transparent',
|
|
28
|
-
},
|
|
29
|
-
'&.cm-editor.cm-focused .cm-nonmatchingBracket': {
|
|
30
|
-
backgroundColor: 'transparent',
|
|
31
|
-
},
|
|
32
|
-
'&dark.cm-editor.cm-focused .cm-matchingBracket': {
|
|
33
|
-
outline: `1px solid ${dark.color.base.border}`,
|
|
34
|
-
},
|
|
35
|
-
'&dark.cm-editor.cm-focused .cm-nonmatchingBracket': {
|
|
36
|
-
outline: `1px solid ${dark.color.base.border}`,
|
|
37
|
-
},
|
|
38
|
-
'&light.cm-editor.cm-focused .cm-matchingBracket': {
|
|
39
|
-
outline: `1px solid ${light.color.base.border}`,
|
|
40
|
-
},
|
|
41
|
-
'&light.cm-editor.cm-focused .cm-nonmatchingBracket': {
|
|
42
|
-
outline: `1px solid ${light.color.base.border}`,
|
|
43
|
-
},
|
|
44
|
-
|
|
45
|
-
// Size and padding of gutter
|
|
46
|
-
'& .cm-lineNumbers .cm-gutterElement': {
|
|
47
|
-
minWidth: `32px !important`,
|
|
48
|
-
padding: `0 8px !important`,
|
|
49
|
-
},
|
|
50
|
-
'& .cm-gutter.cm-foldGutter': {
|
|
51
|
-
width: `0px !important`,
|
|
52
|
-
},
|
|
53
|
-
|
|
54
|
-
// Color of gutter
|
|
55
|
-
'&dark .cm-gutters': {
|
|
56
|
-
color: `${rgba(dark.color.card.enabled.code.fg, 0.5)} !important`,
|
|
57
|
-
borderRight: `1px solid ${rgba(dark.color.base.border, 0.5)}`,
|
|
58
|
-
},
|
|
59
|
-
'&light .cm-gutters': {
|
|
60
|
-
color: `${rgba(light.color.card.enabled.code.fg, 0.5)} !important`,
|
|
61
|
-
borderRight: `1px solid ${rgba(light.color.base.border, 0.5)}`,
|
|
62
|
-
},
|
|
63
|
-
})
|
|
64
|
-
}, [themeCtx])
|
|
65
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import type {Extension} from '@codemirror/state'
|
|
2
|
-
import {tags as t} from '@lezer/highlight'
|
|
3
|
-
import {useTheme} from '@sanity/ui'
|
|
4
|
-
import {rgba} from '@sanity/ui/theme'
|
|
5
|
-
import {createTheme} from '@uiw/codemirror-themes'
|
|
6
|
-
import {useMemo} from 'react'
|
|
7
|
-
|
|
8
|
-
export function useCodeMirrorTheme(): Extension {
|
|
9
|
-
const theme = useTheme()
|
|
10
|
-
|
|
11
|
-
return useMemo(() => {
|
|
12
|
-
const {code: codeFont} = theme.sanity.fonts
|
|
13
|
-
const {base, card, dark, syntax} = theme.sanity.color
|
|
14
|
-
|
|
15
|
-
return createTheme({
|
|
16
|
-
theme: dark ? 'dark' : 'light',
|
|
17
|
-
settings: {
|
|
18
|
-
background: card.enabled.bg,
|
|
19
|
-
foreground: card.enabled.code.fg,
|
|
20
|
-
lineHighlight: card.enabled.bg,
|
|
21
|
-
fontFamily: codeFont.family,
|
|
22
|
-
caret: base.focusRing,
|
|
23
|
-
selection: rgba(base.focusRing, 0.2),
|
|
24
|
-
selectionMatch: rgba(base.focusRing, 0.4),
|
|
25
|
-
gutterBackground: card.disabled.bg,
|
|
26
|
-
gutterForeground: card.disabled.code.fg,
|
|
27
|
-
gutterActiveForeground: card.enabled.fg,
|
|
28
|
-
},
|
|
29
|
-
styles: [
|
|
30
|
-
{
|
|
31
|
-
tag: [t.heading, t.heading2, t.heading3, t.heading4, t.heading5, t.heading6],
|
|
32
|
-
color: card.enabled.fg,
|
|
33
|
-
},
|
|
34
|
-
{tag: t.angleBracket, color: card.enabled.code.fg},
|
|
35
|
-
{tag: t.atom, color: syntax.keyword},
|
|
36
|
-
{tag: t.attributeName, color: syntax.attrName},
|
|
37
|
-
{tag: t.bool, color: syntax.boolean},
|
|
38
|
-
{tag: t.bracket, color: card.enabled.code.fg},
|
|
39
|
-
{tag: t.className, color: syntax.className},
|
|
40
|
-
{tag: t.comment, color: syntax.comment},
|
|
41
|
-
{tag: t.definition(t.typeName), color: syntax.function},
|
|
42
|
-
{
|
|
43
|
-
tag: [
|
|
44
|
-
t.definition(t.variableName),
|
|
45
|
-
t.function(t.variableName),
|
|
46
|
-
t.className,
|
|
47
|
-
t.attributeName,
|
|
48
|
-
],
|
|
49
|
-
color: syntax.function,
|
|
50
|
-
},
|
|
51
|
-
{tag: [t.function(t.propertyName), t.propertyName], color: syntax.function},
|
|
52
|
-
{tag: t.keyword, color: syntax.keyword},
|
|
53
|
-
{tag: t.null, color: syntax.number},
|
|
54
|
-
{tag: t.number, color: syntax.number},
|
|
55
|
-
{tag: t.meta, color: card.enabled.code.fg},
|
|
56
|
-
{tag: t.operator, color: syntax.operator},
|
|
57
|
-
{tag: t.propertyName, color: syntax.property},
|
|
58
|
-
{tag: [t.string, t.special(t.brace)], color: syntax.string},
|
|
59
|
-
{tag: t.tagName, color: syntax.className},
|
|
60
|
-
{tag: t.typeName, color: syntax.keyword},
|
|
61
|
-
],
|
|
62
|
-
})
|
|
63
|
-
}, [theme])
|
|
64
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type {Extension} from '@codemirror/state'
|
|
2
|
-
import {EditorView} from '@codemirror/view'
|
|
3
|
-
import {rem, useTheme} from '@sanity/ui'
|
|
4
|
-
import {useMemo} from 'react'
|
|
5
|
-
|
|
6
|
-
export function useFontSizeExtension(props: {fontSize: number}): Extension {
|
|
7
|
-
const {fontSize: fontSizeProp} = props
|
|
8
|
-
const theme = useTheme()
|
|
9
|
-
|
|
10
|
-
return useMemo(() => {
|
|
11
|
-
const {code: codeFont} = theme.sanity.fonts
|
|
12
|
-
const {fontSize, lineHeight} = codeFont.sizes[fontSizeProp] || codeFont.sizes[2]!
|
|
13
|
-
|
|
14
|
-
return EditorView.baseTheme({
|
|
15
|
-
'&': {
|
|
16
|
-
fontSize: rem(fontSize),
|
|
17
|
-
},
|
|
18
|
-
|
|
19
|
-
'& .cm-scroller': {
|
|
20
|
-
lineHeight: `${lineHeight / fontSize} !important`,
|
|
21
|
-
},
|
|
22
|
-
})
|
|
23
|
-
}, [fontSizeProp, theme])
|
|
24
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/** @jest-environment jsdom */
|
|
2
|
-
|
|
3
|
-
import {studioTheme, ThemeProvider} from '@sanity/ui'
|
|
4
|
-
import {act, render} from '@testing-library/react'
|
|
5
|
-
import {Suspense} from 'react'
|
|
6
|
-
|
|
7
|
-
import {CodeMirrorProxy, useMounted} from './useCodeMirror'
|
|
8
|
-
|
|
9
|
-
describe('useCodeMirror - client', () => {
|
|
10
|
-
let rafMock: jest.SpyInstance<number, [FrameRequestCallback]>
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
rafMock = jest
|
|
14
|
-
.spyOn(window, 'requestAnimationFrame')
|
|
15
|
-
.mockImplementation((callback: FrameRequestCallback): number => {
|
|
16
|
-
try {
|
|
17
|
-
callback(0)
|
|
18
|
-
} catch {
|
|
19
|
-
// CodeMirror does some mesurement shenanigance that json dont support
|
|
20
|
-
// we just let it crash silently
|
|
21
|
-
}
|
|
22
|
-
return 0
|
|
23
|
-
})
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
afterEach(() => {
|
|
27
|
-
rafMock.mockRestore()
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
it('should render suspended codemirror editor', async () => {
|
|
31
|
-
const TestComponent = () => {
|
|
32
|
-
const mounted = useMounted()
|
|
33
|
-
return (
|
|
34
|
-
<Suspense fallback={'loading'}>
|
|
35
|
-
{mounted && (
|
|
36
|
-
<ThemeProvider theme={studioTheme}>
|
|
37
|
-
<CodeMirrorProxy languageMode={'tsx'} />
|
|
38
|
-
</ThemeProvider>
|
|
39
|
-
)}
|
|
40
|
-
</Suspense>
|
|
41
|
-
)
|
|
42
|
-
}
|
|
43
|
-
let container: HTMLElement | undefined
|
|
44
|
-
await act(async () => {
|
|
45
|
-
const result = render(<TestComponent />)
|
|
46
|
-
container = result.container
|
|
47
|
-
})
|
|
48
|
-
expect(container).toBeTruthy()
|
|
49
|
-
expect(container!.querySelector('.cm-theme')).toBeTruthy()
|
|
50
|
-
})
|
|
51
|
-
})
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import {renderToString} from 'react-dom/server'
|
|
2
|
-
|
|
3
|
-
import {useMounted} from './useCodeMirror'
|
|
4
|
-
|
|
5
|
-
describe('useCodeMirror - server', () => {
|
|
6
|
-
it('should render null to string (and not throw and Error)', () => {
|
|
7
|
-
const TestComponent = () => {
|
|
8
|
-
const mounted = useMounted()
|
|
9
|
-
if (!mounted) {
|
|
10
|
-
return null
|
|
11
|
-
}
|
|
12
|
-
throw new Error('editor should always be null in envs without window')
|
|
13
|
-
}
|
|
14
|
-
const serverString = renderToString(<TestComponent />)
|
|
15
|
-
|
|
16
|
-
expect(serverString).toEqual('')
|
|
17
|
-
})
|
|
18
|
-
})
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import {lazy, startTransition, useEffect, useState} from 'react'
|
|
2
|
-
|
|
3
|
-
export const CodeMirrorProxy = lazy(() => import('./CodeMirrorProxy'))
|
|
4
|
-
|
|
5
|
-
export function useMounted() {
|
|
6
|
-
const [mounted, setMounted] = useState(false)
|
|
7
|
-
useEffect(() => {
|
|
8
|
-
requestAnimationFrame(() => startTransition(() => setMounted(true)))
|
|
9
|
-
}, [])
|
|
10
|
-
return mounted
|
|
11
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import {useMemo} from 'react'
|
|
2
|
-
|
|
3
|
-
import {LANGUAGE_ALIASES, SUPPORTED_LANGUAGES} from '../config'
|
|
4
|
-
import type {CodeInputLanguage, CodeInputValue, CodeSchemaType} from '../types'
|
|
5
|
-
|
|
6
|
-
export const defaultLanguageMode = 'text'
|
|
7
|
-
|
|
8
|
-
export function useLanguageMode(
|
|
9
|
-
schemaType: CodeSchemaType,
|
|
10
|
-
value?: CodeInputValue,
|
|
11
|
-
): {
|
|
12
|
-
language: string
|
|
13
|
-
languageMode: string
|
|
14
|
-
languages: CodeInputLanguage[]
|
|
15
|
-
} {
|
|
16
|
-
const languages = useLanguageAlternatives(schemaType)
|
|
17
|
-
const fixedLanguage = schemaType.options?.language
|
|
18
|
-
const language = value?.language ?? fixedLanguage ?? defaultLanguageMode
|
|
19
|
-
|
|
20
|
-
// the language config from the schema
|
|
21
|
-
const configured = languages.find((entry) => entry.value === language)
|
|
22
|
-
const languageMode = configured?.mode ?? resolveAliasedLanguage(language) ?? defaultLanguageMode
|
|
23
|
-
|
|
24
|
-
return {language, languageMode, languages}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function resolveAliasedLanguage(lang?: string) {
|
|
28
|
-
return (lang && LANGUAGE_ALIASES[lang]) ?? lang
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function useLanguageAlternatives(type: CodeSchemaType) {
|
|
32
|
-
return useMemo((): CodeInputLanguage[] => {
|
|
33
|
-
const languageAlternatives = type.options?.languageAlternatives
|
|
34
|
-
if (!languageAlternatives) {
|
|
35
|
-
return SUPPORTED_LANGUAGES
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (!Array.isArray(languageAlternatives)) {
|
|
39
|
-
throw new Error(
|
|
40
|
-
`'options.languageAlternatives' should be an array, got ${typeof languageAlternatives}`,
|
|
41
|
-
)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return languageAlternatives.reduce((acc: CodeInputLanguage[], {title, value: val, mode}) => {
|
|
45
|
-
const alias = LANGUAGE_ALIASES[val]
|
|
46
|
-
if (alias) {
|
|
47
|
-
// eslint-disable-next-line no-console
|
|
48
|
-
console.warn(
|
|
49
|
-
`'options.languageAlternatives' lists a language with value "%s", which is an alias of "%s" - please replace the value to read "%s"`,
|
|
50
|
-
val,
|
|
51
|
-
alias,
|
|
52
|
-
alias,
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
return acc.concat({title, value: alias, mode: mode})
|
|
56
|
-
}
|
|
57
|
-
return acc.concat({title, value: val, mode})
|
|
58
|
-
}, [])
|
|
59
|
-
}, [type])
|
|
60
|
-
}
|
package/src/config.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import type {CodeInputLanguage} from './types'
|
|
2
|
-
|
|
3
|
-
// NOTE: MAKE SURE THESE ALIGN WITH CODE MODES IN ./codemirror/defaultCodeModes.ts
|
|
4
|
-
export const SUPPORTED_LANGUAGES: CodeInputLanguage[] = [
|
|
5
|
-
{title: 'Batch file', value: 'batchfile'},
|
|
6
|
-
{title: 'C#', value: 'csharp'},
|
|
7
|
-
{title: 'CSS', value: 'css'},
|
|
8
|
-
{title: 'Go', value: 'golang'},
|
|
9
|
-
{title: 'GROQ', value: 'groq'},
|
|
10
|
-
{title: 'HTML', value: 'html'},
|
|
11
|
-
{title: 'Java', value: 'java'},
|
|
12
|
-
{title: 'JavaScript', value: 'javascript'},
|
|
13
|
-
{title: 'JSON', value: 'json'},
|
|
14
|
-
{title: 'JSX', value: 'jsx'},
|
|
15
|
-
{title: 'Markdown', value: 'markdown'},
|
|
16
|
-
{title: 'MySQL', value: 'mysql'},
|
|
17
|
-
{title: 'PHP', value: 'php'},
|
|
18
|
-
{title: 'Plain text', value: 'text'},
|
|
19
|
-
{title: 'Python', value: 'python'},
|
|
20
|
-
{title: 'Ruby', value: 'ruby'},
|
|
21
|
-
{title: 'SASS', value: 'sass'},
|
|
22
|
-
{title: 'SCSS', value: 'scss'},
|
|
23
|
-
{title: 'sh', value: 'sh'},
|
|
24
|
-
{title: 'SQL', value: 'sql'},
|
|
25
|
-
{title: 'TSX', value: 'tsx'},
|
|
26
|
-
{title: 'TypeScript', value: 'typescript'},
|
|
27
|
-
{title: 'XML', value: 'xml'},
|
|
28
|
-
{title: 'YAML', value: 'yaml'},
|
|
29
|
-
]
|
|
30
|
-
|
|
31
|
-
export const LANGUAGE_ALIASES: Record<string, string | undefined> = {js: 'javascript'}
|
|
32
|
-
|
|
33
|
-
export const PATH_LANGUAGE = ['language']
|
|
34
|
-
export const PATH_CODE = ['code']
|
|
35
|
-
export const PATH_FILENAME = ['filename']
|