@sanity/code-input 2.29.3 → 2.29.4-purple-unicorn.509
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/CodeInput.js +7 -0
- package/deprecatedSchema.js +7 -0
- package/lib/CodeInput.cjs +42 -0
- package/lib/CodeInput.cjs.map +1 -0
- package/lib/CodeInput.js +36 -333
- package/lib/CodeInput.js.map +1 -0
- package/lib/_CodeInput-1b58302e.js +893 -0
- package/lib/_CodeInput-1b58302e.js.map +1 -0
- package/lib/_CodeInput-95db5e9a.cjs +923 -0
- package/lib/_CodeInput-95db5e9a.cjs.map +1 -0
- package/lib/_reExport.js +19 -0
- package/lib/deprecatedSchema.cjs +23 -0
- package/lib/deprecatedSchema.cjs.map +1 -0
- package/lib/deprecatedSchema.js +19 -24
- package/lib/deprecatedSchema.js.map +1 -0
- package/lib/dts/src/CodeInput.d.ts +20 -0
- package/lib/dts/src/CodeInput.d.ts.map +1 -0
- package/lib/dts/src/CodeInput.js +201 -0
- package/lib/dts/src/CodeInput.js.map +1 -0
- package/{dist/dts → lib/dts/src}/PreviewCode.d.ts +8 -8
- package/lib/dts/src/PreviewCode.d.ts.map +1 -0
- package/lib/dts/src/PreviewCode.js +50 -0
- package/lib/dts/src/PreviewCode.js.map +1 -0
- package/lib/dts/src/__workshop__/dev.d.ts +3 -0
- package/lib/dts/src/__workshop__/dev.d.ts.map +1 -0
- package/lib/dts/src/__workshop__/dev.js +19 -0
- package/lib/dts/src/__workshop__/dev.js.map +1 -0
- package/lib/dts/src/__workshop__/index.d.ts +3 -0
- package/lib/dts/src/__workshop__/index.d.ts.map +1 -0
- package/lib/dts/src/__workshop__/index.js +10 -0
- package/lib/dts/src/__workshop__/index.js.map +1 -0
- package/{dist/dts → lib/dts/src}/config.d.ts +15 -15
- package/lib/dts/src/config.d.ts.map +1 -0
- package/lib/dts/src/config.js +38 -0
- package/lib/dts/src/config.js.map +1 -0
- package/{dist/dts → lib/dts/src}/createHighlightMarkers.d.ts +3 -3
- package/lib/dts/src/createHighlightMarkers.d.ts.map +1 -0
- package/lib/dts/src/createHighlightMarkers.js +22 -0
- package/lib/dts/src/createHighlightMarkers.js.map +1 -0
- package/{dist/dts → lib/dts/src}/deprecatedSchema.d.ts +11 -11
- package/lib/dts/src/deprecatedSchema.d.ts.map +1 -0
- package/lib/dts/src/deprecatedSchema.js +19 -0
- package/lib/dts/src/deprecatedSchema.js.map +1 -0
- package/{dist/dts → lib/dts/src}/editorSupport.d.ts +27 -27
- package/lib/dts/src/editorSupport.d.ts.map +1 -0
- package/lib/dts/src/editorSupport.js +32 -0
- package/lib/dts/src/editorSupport.js.map +1 -0
- package/{dist/dts → lib/dts/src}/getMedia.d.ts +2 -2
- package/lib/dts/src/getMedia.d.ts.map +1 -0
- package/lib/dts/src/getMedia.js +35 -0
- package/lib/dts/src/getMedia.js.map +1 -0
- package/lib/dts/src/groq.d.ts +2 -0
- package/lib/dts/src/groq.d.ts.map +1 -0
- package/lib/dts/src/groq.js +612 -0
- package/lib/dts/src/groq.js.map +1 -0
- package/lib/dts/src/index.d.ts +4 -0
- package/lib/dts/src/index.d.ts.map +1 -0
- package/lib/dts/src/index.js +7 -0
- package/lib/dts/src/index.js.map +1 -0
- package/lib/dts/src/schema.d.ts +46 -0
- package/lib/dts/src/schema.d.ts.map +1 -0
- package/lib/dts/src/schema.js +59 -0
- package/lib/dts/src/schema.js.map +1 -0
- package/{dist/dts → lib/dts/src}/types.d.ts +28 -28
- package/lib/dts/src/types.d.ts.map +1 -0
- package/lib/dts/src/types.js +2 -0
- package/lib/dts/src/types.js.map +1 -0
- package/lib/dts/tsconfig.tsbuildinfo +1 -0
- package/lib/index.cjs +48 -0
- package/lib/index.cjs.map +1 -0
- package/lib/index.js +46 -0
- package/lib/index.js.map +1 -0
- package/lib/schema.cjs +177 -0
- package/lib/schema.cjs.map +1 -0
- package/lib/schema.js +162 -62
- package/lib/schema.js.map +1 -0
- package/package.json +63 -11
- package/schema.js +7 -0
- package/src/CodeInput.tsx +427 -0
- package/src/PreviewCode.tsx +89 -0
- package/src/__workshop__/dev.tsx +34 -0
- package/src/__workshop__/index.ts +10 -0
- package/src/config.ts +45 -0
- package/src/createHighlightMarkers.ts +24 -0
- package/src/deprecatedSchema.ts +19 -0
- package/src/editorSupport.ts +33 -0
- package/src/getMedia.tsx +95 -0
- package/src/groq.ts +630 -0
- package/src/index.ts +11 -0
- package/src/schema.tsx +69 -0
- package/src/types.ts +26 -0
- package/dist/dts/CodeInput.d.ts +0 -23
- package/dist/dts/CodeInput.d.ts.map +0 -1
- package/dist/dts/PreviewCode.d.ts.map +0 -1
- package/dist/dts/config.d.ts.map +0 -1
- package/dist/dts/createHighlightMarkers.d.ts.map +0 -1
- package/dist/dts/deprecatedSchema.d.ts.map +0 -1
- package/dist/dts/editorSupport.d.ts.map +0 -1
- package/dist/dts/getMedia.d.ts.map +0 -1
- package/dist/dts/groq.d.ts +0 -376
- package/dist/dts/groq.d.ts.map +0 -1
- package/dist/dts/schema.d.ts +0 -44
- package/dist/dts/schema.d.ts.map +0 -1
- package/dist/dts/types.d.ts.map +0 -1
- package/lib/@types/css.d.js +0 -1
- package/lib/PreviewCode.js +0 -79
- package/lib/config.js +0 -103
- package/lib/createHighlightMarkers.js +0 -31
- package/lib/editorSupport.js +0 -55
- package/lib/getMedia.js +0 -110
- package/lib/groq.js +0 -414
- package/lib/types.js +0 -5
- package/tsconfig.json +0 -26
package/package.json
CHANGED
|
@@ -1,13 +1,57 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/code-input",
|
|
3
|
-
"version": "2.29.
|
|
3
|
+
"version": "2.29.4-purple-unicorn.509+183505a0b3",
|
|
4
4
|
"description": "Ace editor for editing code",
|
|
5
|
-
"
|
|
6
|
-
"
|
|
5
|
+
"source": "./src/index.ts",
|
|
6
|
+
"main": "./lib/index.cjs",
|
|
7
|
+
"module": "./lib/index.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"source": "./src/index.ts",
|
|
11
|
+
"require": "./lib/index.cjs",
|
|
12
|
+
"default": "./lib/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./CodeInput": {
|
|
15
|
+
"source": "./src/CodeInput.tsx",
|
|
16
|
+
"require": "./lib/CodeInput.cjs",
|
|
17
|
+
"default": "./lib/CodeInput.js"
|
|
18
|
+
},
|
|
19
|
+
"./schema": {
|
|
20
|
+
"source": "./src/schema.tsx",
|
|
21
|
+
"require": "./lib/schema.cjs",
|
|
22
|
+
"default": "./lib/schema.js"
|
|
23
|
+
},
|
|
24
|
+
"./deprecatedSchema": {
|
|
25
|
+
"source": "./src/deprecatedSchema.ts",
|
|
26
|
+
"require": "./lib/deprecatedSchema.cjs",
|
|
27
|
+
"default": "./lib/deprecatedSchema.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"types": "./lib/dts/src/index.d.ts",
|
|
31
|
+
"//": "the typesVersion config below is a workaround for TypeScript's lack of support for package exports",
|
|
32
|
+
"typesVersions": {
|
|
33
|
+
"*": {
|
|
34
|
+
"CodeInput": [
|
|
35
|
+
"./lib/dts/src/CodeInput.d.ts"
|
|
36
|
+
],
|
|
37
|
+
"schema": [
|
|
38
|
+
"./lib/dts/src/schema.d.ts"
|
|
39
|
+
],
|
|
40
|
+
"deprecatedSchema": [
|
|
41
|
+
"./lib/dts/src/deprecatedSchema.d.ts"
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
},
|
|
7
45
|
"author": "Sanity.io <hello@sanity.io>",
|
|
8
46
|
"license": "MIT",
|
|
9
47
|
"scripts": {
|
|
10
|
-
"
|
|
48
|
+
"build": "../../../bin/pkg-utils bundle",
|
|
49
|
+
"clean": "rimraf lib",
|
|
50
|
+
"exports:check": "../../../bin/pkg-utils exports-check",
|
|
51
|
+
"exports:clean": "../../../bin/pkg-utils exports-clean",
|
|
52
|
+
"exports:generate": "../../../bin/pkg-utils exports-generate",
|
|
53
|
+
"postbuild": "run-s exports:generate",
|
|
54
|
+
"watch": "../../../bin/pkg-utils bundle --watch"
|
|
11
55
|
},
|
|
12
56
|
"keywords": [
|
|
13
57
|
"sanity",
|
|
@@ -21,18 +65,26 @@
|
|
|
21
65
|
],
|
|
22
66
|
"dependencies": {
|
|
23
67
|
"@reach/auto-id": "^0.13.2",
|
|
24
|
-
"@sanity/
|
|
25
|
-
"@sanity/
|
|
26
|
-
"@sanity/types": "2.29.3",
|
|
27
|
-
"@sanity/ui": "^0.37.2",
|
|
28
|
-
"@sanity/util": "2.29.3",
|
|
68
|
+
"@sanity/icons": "^1.2.8",
|
|
69
|
+
"@sanity/ui": "^0.37.8",
|
|
29
70
|
"ace-builds": "^1.4.13",
|
|
30
71
|
"react-ace": "^9.5.0"
|
|
31
72
|
},
|
|
32
73
|
"devDependencies": {
|
|
33
|
-
"
|
|
74
|
+
"@sanity/base": "2.29.4-purple-unicorn.509+183505a0b3",
|
|
75
|
+
"@sanity/form-builder": "2.29.3",
|
|
76
|
+
"@sanity/types": "2.29.4-purple-unicorn.509+183505a0b3",
|
|
77
|
+
"@sanity/ui-workshop": "^0.4.3",
|
|
78
|
+
"@sanity/util": "2.29.4-purple-unicorn.509+183505a0b3",
|
|
79
|
+
"react": "17.0.2",
|
|
80
|
+
"rimraf": "^3.0.2",
|
|
81
|
+
"styled-components": "^5.2.0"
|
|
34
82
|
},
|
|
35
83
|
"peerDependencies": {
|
|
84
|
+
"@sanity/base": "^2.14",
|
|
85
|
+
"@sanity/form-builder": "^2.27",
|
|
86
|
+
"@sanity/types": "^2.14",
|
|
87
|
+
"@sanity/util": "2.27",
|
|
36
88
|
"prop-types": "^15.6 || ^16",
|
|
37
89
|
"react": "^16.9 || ^17",
|
|
38
90
|
"styled-components": "^5.2.0"
|
|
@@ -46,5 +98,5 @@
|
|
|
46
98
|
"url": "git+https://github.com/sanity-io/sanity.git",
|
|
47
99
|
"directory": "packages/@sanity/code-input"
|
|
48
100
|
},
|
|
49
|
-
"gitHead": "
|
|
101
|
+
"gitHead": "183505a0b3ea400181bff4f4370c146374b58068"
|
|
50
102
|
}
|
package/schema.js
ADDED
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
import React, {useCallback, useEffect, useImperativeHandle, useRef} from 'react'
|
|
2
|
+
import {FormField, FormFieldSet, ChangeIndicatorProvider} from '@sanity/base/_unstable'
|
|
3
|
+
import {PatchEvent, set, unset, setIfMissing, FormInputProps} from '@sanity/base/form'
|
|
4
|
+
import {ObjectSchemaType} from '@sanity/types'
|
|
5
|
+
import {Card, Select, Stack, TextInput} from '@sanity/ui'
|
|
6
|
+
import * as PathUtils from '@sanity/util/paths'
|
|
7
|
+
import AceEditor from 'react-ace'
|
|
8
|
+
import styled from 'styled-components'
|
|
9
|
+
import {useId} from '@reach/auto-id'
|
|
10
|
+
import createHighlightMarkers, {highlightMarkersCSS} from './createHighlightMarkers'
|
|
11
|
+
import {CodeInputLanguage, CodeInputValue} from './types'
|
|
12
|
+
/* eslint-disable-next-line import/no-unassigned-import */
|
|
13
|
+
import './editorSupport'
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
ACE_EDITOR_PROPS,
|
|
17
|
+
ACE_SET_OPTIONS,
|
|
18
|
+
DEFAULT_THEME,
|
|
19
|
+
LANGUAGE_ALIASES,
|
|
20
|
+
PATH_CODE,
|
|
21
|
+
PATH_FILENAME,
|
|
22
|
+
PATH_LANGUAGE,
|
|
23
|
+
SUPPORTED_LANGUAGES,
|
|
24
|
+
SUPPORTED_THEMES,
|
|
25
|
+
} from './config'
|
|
26
|
+
|
|
27
|
+
export type {CodeInputLanguage, CodeInputValue} from './types'
|
|
28
|
+
|
|
29
|
+
const EditorContainer = styled(Card)`
|
|
30
|
+
position: relative;
|
|
31
|
+
box-sizing: border-box;
|
|
32
|
+
overflow: hidden;
|
|
33
|
+
z-index: 0;
|
|
34
|
+
|
|
35
|
+
.ace_editor {
|
|
36
|
+
font-family: ${({theme}) => theme.sanity.fonts.code.family};
|
|
37
|
+
font-size: ${({theme}) => theme.sanity.fonts.code.sizes[1]};
|
|
38
|
+
line-height: inherit;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
${highlightMarkersCSS}
|
|
42
|
+
|
|
43
|
+
&:not([disabled]):not([readonly]) {
|
|
44
|
+
&:focus,
|
|
45
|
+
&:focus-within {
|
|
46
|
+
box-shadow: 0 0 0 2px ${({theme}) => theme.sanity.color.base.focusRing};
|
|
47
|
+
background-color: ${({theme}) => theme.sanity.color.base.bg};
|
|
48
|
+
border-color: ${({theme}) => theme.sanity.color.base.focusRing};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
`
|
|
52
|
+
|
|
53
|
+
export type CodeSchemaType = Omit<ObjectSchemaType, 'options'> & {
|
|
54
|
+
options: {
|
|
55
|
+
theme?: string
|
|
56
|
+
languageAlternatives: CodeInputLanguage[]
|
|
57
|
+
language: string
|
|
58
|
+
withFilename?: boolean
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type CodeInputProps = FormInputProps<CodeInputValue, CodeSchemaType>
|
|
63
|
+
|
|
64
|
+
// Returns a string with the mode name if supported (because aliases), otherwise false
|
|
65
|
+
function isSupportedLanguage(mode: string) {
|
|
66
|
+
const alias = LANGUAGE_ALIASES[mode]
|
|
67
|
+
|
|
68
|
+
if (alias) {
|
|
69
|
+
return alias
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const isSupported = SUPPORTED_LANGUAGES.find((lang) => lang.value === mode)
|
|
73
|
+
if (isSupported) {
|
|
74
|
+
return mode
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return false
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const CodeInput = React.forwardRef(
|
|
81
|
+
(props: CodeInputProps, ref: React.ForwardedRef<{focus: () => void}>) => {
|
|
82
|
+
const aceEditorRef = useRef<any>()
|
|
83
|
+
const aceEditorId = useId()
|
|
84
|
+
|
|
85
|
+
const {
|
|
86
|
+
onFocus,
|
|
87
|
+
onChange,
|
|
88
|
+
onBlur,
|
|
89
|
+
compareValue,
|
|
90
|
+
value,
|
|
91
|
+
presence,
|
|
92
|
+
type,
|
|
93
|
+
level = 0,
|
|
94
|
+
readOnly,
|
|
95
|
+
focusPath,
|
|
96
|
+
} = props
|
|
97
|
+
|
|
98
|
+
useImperativeHandle(ref, () => ({
|
|
99
|
+
focus: () => {
|
|
100
|
+
aceEditorRef?.current?.editor?.focus()
|
|
101
|
+
},
|
|
102
|
+
}))
|
|
103
|
+
|
|
104
|
+
const handleLanguageFocus = useCallback(() => {
|
|
105
|
+
onFocus(PATH_LANGUAGE)
|
|
106
|
+
}, [onFocus])
|
|
107
|
+
|
|
108
|
+
const handleCodeFocus = useCallback(() => {
|
|
109
|
+
onFocus(PATH_CODE)
|
|
110
|
+
}, [onFocus])
|
|
111
|
+
|
|
112
|
+
const handleFilenameFocus = useCallback(() => {
|
|
113
|
+
onFocus(PATH_FILENAME)
|
|
114
|
+
}, [onFocus])
|
|
115
|
+
|
|
116
|
+
const handleFilenameChange = useCallback(
|
|
117
|
+
(event: React.ChangeEvent<HTMLInputElement>) => {
|
|
118
|
+
const val = event.target.value
|
|
119
|
+
const path = PATH_FILENAME
|
|
120
|
+
|
|
121
|
+
onChange(
|
|
122
|
+
PatchEvent.from([setIfMissing({_type: type.name}), val ? set(val, path) : unset(path)])
|
|
123
|
+
)
|
|
124
|
+
},
|
|
125
|
+
[onChange, type.name]
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
const getTheme = useCallback(() => {
|
|
129
|
+
const preferredTheme = type.options?.theme
|
|
130
|
+
return preferredTheme && SUPPORTED_THEMES.find((theme) => theme === preferredTheme)
|
|
131
|
+
? preferredTheme
|
|
132
|
+
: DEFAULT_THEME
|
|
133
|
+
}, [type])
|
|
134
|
+
|
|
135
|
+
const handleToggleSelectLine = useCallback(
|
|
136
|
+
(lineNumber: number) => {
|
|
137
|
+
const editorSession = aceEditorRef.current?.editor?.getSession()
|
|
138
|
+
const backgroundMarkers = editorSession?.getMarkers(true)
|
|
139
|
+
const currentHighlightedLines = Object.keys(backgroundMarkers)
|
|
140
|
+
.filter((key) => backgroundMarkers[key].type === 'screenLine')
|
|
141
|
+
.map((key) => backgroundMarkers[key].range.start.row)
|
|
142
|
+
const currentIndex = currentHighlightedLines.indexOf(lineNumber)
|
|
143
|
+
if (currentIndex > -1) {
|
|
144
|
+
// toggle remove
|
|
145
|
+
currentHighlightedLines.splice(currentIndex, 1)
|
|
146
|
+
} else {
|
|
147
|
+
// toggle add
|
|
148
|
+
currentHighlightedLines.push(lineNumber)
|
|
149
|
+
currentHighlightedLines.sort()
|
|
150
|
+
}
|
|
151
|
+
onChange(
|
|
152
|
+
PatchEvent.from(
|
|
153
|
+
set(
|
|
154
|
+
currentHighlightedLines.map(
|
|
155
|
+
(line) =>
|
|
156
|
+
// ace starts at line (row) 0, but we store it starting at line 1
|
|
157
|
+
line + 1
|
|
158
|
+
),
|
|
159
|
+
['highlightedLines']
|
|
160
|
+
)
|
|
161
|
+
)
|
|
162
|
+
)
|
|
163
|
+
},
|
|
164
|
+
[aceEditorRef, onChange]
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
const handleGutterMouseDown = useCallback(
|
|
168
|
+
(event: any) => {
|
|
169
|
+
const target = event.domEvent.target
|
|
170
|
+
if (target.classList.contains('ace_gutter-cell')) {
|
|
171
|
+
const row = event.getDocumentPosition().row
|
|
172
|
+
handleToggleSelectLine(row)
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
[handleToggleSelectLine]
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
useEffect(() => {
|
|
179
|
+
const editor = aceEditorRef?.current?.editor
|
|
180
|
+
return () => {
|
|
181
|
+
editor?.session?.removeListener('guttermousedown', handleGutterMouseDown)
|
|
182
|
+
}
|
|
183
|
+
}, [aceEditorRef, handleGutterMouseDown])
|
|
184
|
+
|
|
185
|
+
const handleEditorLoad = useCallback(
|
|
186
|
+
(editor: any) => {
|
|
187
|
+
editor?.on('guttermousedown', handleGutterMouseDown)
|
|
188
|
+
},
|
|
189
|
+
[handleGutterMouseDown]
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
const getLanguageAlternatives = useCallback((): {
|
|
193
|
+
title: string
|
|
194
|
+
value: string
|
|
195
|
+
mode?: string
|
|
196
|
+
}[] => {
|
|
197
|
+
const languageAlternatives = type.options?.languageAlternatives
|
|
198
|
+
if (!languageAlternatives) {
|
|
199
|
+
return SUPPORTED_LANGUAGES
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (!Array.isArray(languageAlternatives)) {
|
|
203
|
+
throw new Error(
|
|
204
|
+
`'options.languageAlternatives' should be an array, got ${typeof languageAlternatives}`
|
|
205
|
+
)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return languageAlternatives.reduce((acc: CodeInputLanguage[], {title, value: val, mode}) => {
|
|
209
|
+
const alias = LANGUAGE_ALIASES[val]
|
|
210
|
+
if (alias) {
|
|
211
|
+
// eslint-disable-next-line no-console
|
|
212
|
+
console.warn(
|
|
213
|
+
`'options.languageAlternatives' lists a language with value "%s", which is an alias of "%s" - please replace the value to read "%s"`,
|
|
214
|
+
val,
|
|
215
|
+
alias,
|
|
216
|
+
alias
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
return acc.concat({title, value: alias, mode: mode})
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (!mode && !SUPPORTED_LANGUAGES.find((lang) => lang.value === val)) {
|
|
223
|
+
// eslint-disable-next-line no-console
|
|
224
|
+
console.warn(
|
|
225
|
+
`'options.languageAlternatives' lists a language which is not supported: "%s", syntax highlighting will be disabled.`,
|
|
226
|
+
val
|
|
227
|
+
)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return acc.concat({title, value: val, mode})
|
|
231
|
+
}, [])
|
|
232
|
+
}, [type])
|
|
233
|
+
|
|
234
|
+
const handleCodeChange = useCallback(
|
|
235
|
+
(code: string) => {
|
|
236
|
+
const path = PATH_CODE
|
|
237
|
+
const fixedLanguage = type.options?.language
|
|
238
|
+
|
|
239
|
+
onChange(
|
|
240
|
+
PatchEvent.from([
|
|
241
|
+
setIfMissing({_type: type.name, language: fixedLanguage}),
|
|
242
|
+
code ? set(code, path) : unset(path),
|
|
243
|
+
])
|
|
244
|
+
)
|
|
245
|
+
},
|
|
246
|
+
[onChange, type]
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
const handleLanguageChange = useCallback(
|
|
250
|
+
(event: React.ChangeEvent<HTMLSelectElement>) => {
|
|
251
|
+
const val = event.currentTarget.value
|
|
252
|
+
const path = PATH_LANGUAGE
|
|
253
|
+
|
|
254
|
+
onChange(
|
|
255
|
+
PatchEvent.from([setIfMissing({_type: type.name}), val ? set(val, path) : unset(path)])
|
|
256
|
+
)
|
|
257
|
+
},
|
|
258
|
+
[onChange, type.name]
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
const languages = getLanguageAlternatives().slice()
|
|
262
|
+
|
|
263
|
+
const selectedLanguage = props?.value?.language
|
|
264
|
+
? languages.find((item: {value: string | undefined}) => item.value === props?.value?.language)
|
|
265
|
+
: undefined
|
|
266
|
+
|
|
267
|
+
const languageField = type.fields.find((field) => field.name === 'language')
|
|
268
|
+
const filenameField = type.fields.find((field) => field.name === 'filename')
|
|
269
|
+
|
|
270
|
+
const languageCompareValue = PathUtils.get(compareValue, PATH_LANGUAGE)
|
|
271
|
+
const codeCompareValue = PathUtils.get(compareValue, PATH_CODE)
|
|
272
|
+
const filenameCompareValue = PathUtils.get(compareValue, PATH_FILENAME)
|
|
273
|
+
|
|
274
|
+
const languagePresence = presence.filter((presenceItem) =>
|
|
275
|
+
PathUtils.startsWith(PATH_LANGUAGE, presenceItem.path)
|
|
276
|
+
)
|
|
277
|
+
const codePresence = presence.filter((presenceItem) =>
|
|
278
|
+
PathUtils.startsWith(PATH_CODE, presenceItem.path)
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
const filenamePresence = presence.filter((presenceItem) =>
|
|
282
|
+
PathUtils.startsWith(PATH_FILENAME, presenceItem.path)
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
const renderEditor = useCallback(() => {
|
|
286
|
+
const fixedLanguage = type.options?.language
|
|
287
|
+
|
|
288
|
+
const language = value?.language || fixedLanguage
|
|
289
|
+
|
|
290
|
+
// the language config from the schema
|
|
291
|
+
const configured = languages.find((entry) => entry.value === language)
|
|
292
|
+
|
|
293
|
+
// is the language officially supported (e.g. we import the mode by default)
|
|
294
|
+
const supported = language && isSupportedLanguage(language)
|
|
295
|
+
|
|
296
|
+
const mode = configured?.mode || (supported ? language : 'text')
|
|
297
|
+
|
|
298
|
+
return (
|
|
299
|
+
<EditorContainer radius={1} shadow={1} readOnly={readOnly}>
|
|
300
|
+
<AceEditor
|
|
301
|
+
ref={aceEditorRef}
|
|
302
|
+
mode={mode}
|
|
303
|
+
theme={getTheme()}
|
|
304
|
+
width="100%"
|
|
305
|
+
onChange={handleCodeChange}
|
|
306
|
+
name={`editor-${aceEditorId}`}
|
|
307
|
+
value={(value && value.code) || ''}
|
|
308
|
+
markers={
|
|
309
|
+
value && value.highlightedLines
|
|
310
|
+
? createHighlightMarkers(value.highlightedLines)
|
|
311
|
+
: undefined
|
|
312
|
+
}
|
|
313
|
+
onLoad={handleEditorLoad}
|
|
314
|
+
readOnly={readOnly}
|
|
315
|
+
tabSize={2}
|
|
316
|
+
wrapEnabled
|
|
317
|
+
setOptions={ACE_SET_OPTIONS}
|
|
318
|
+
editorProps={ACE_EDITOR_PROPS}
|
|
319
|
+
onFocus={handleCodeFocus}
|
|
320
|
+
onBlur={onBlur}
|
|
321
|
+
/>
|
|
322
|
+
</EditorContainer>
|
|
323
|
+
)
|
|
324
|
+
}, [
|
|
325
|
+
aceEditorId,
|
|
326
|
+
getTheme,
|
|
327
|
+
handleCodeChange,
|
|
328
|
+
handleCodeFocus,
|
|
329
|
+
handleEditorLoad,
|
|
330
|
+
languages,
|
|
331
|
+
onBlur,
|
|
332
|
+
readOnly,
|
|
333
|
+
type,
|
|
334
|
+
value,
|
|
335
|
+
])
|
|
336
|
+
|
|
337
|
+
if (type.options?.language) {
|
|
338
|
+
return (
|
|
339
|
+
<ChangeIndicatorProvider
|
|
340
|
+
path={PATH_CODE}
|
|
341
|
+
focusPath={focusPath}
|
|
342
|
+
value={value?.code}
|
|
343
|
+
compareValue={codeCompareValue}
|
|
344
|
+
>
|
|
345
|
+
<FormFieldSet title={type.title} description={type.description} level={level}>
|
|
346
|
+
{renderEditor()}
|
|
347
|
+
</FormFieldSet>
|
|
348
|
+
</ChangeIndicatorProvider>
|
|
349
|
+
)
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return (
|
|
353
|
+
<FormFieldSet
|
|
354
|
+
title={type.title}
|
|
355
|
+
description={type.description}
|
|
356
|
+
level={level}
|
|
357
|
+
__unstable_changeIndicator={false}
|
|
358
|
+
>
|
|
359
|
+
<Stack space={3}>
|
|
360
|
+
<ChangeIndicatorProvider
|
|
361
|
+
path={PATH_LANGUAGE}
|
|
362
|
+
focusPath={focusPath}
|
|
363
|
+
value={selectedLanguage?.value}
|
|
364
|
+
compareValue={languageCompareValue}
|
|
365
|
+
>
|
|
366
|
+
<FormField
|
|
367
|
+
level={level + 1}
|
|
368
|
+
label={languageField?.type.title || 'Language'}
|
|
369
|
+
__unstable_presence={languagePresence}
|
|
370
|
+
>
|
|
371
|
+
<Select
|
|
372
|
+
onChange={handleLanguageChange}
|
|
373
|
+
readOnly={readOnly}
|
|
374
|
+
value={selectedLanguage?.value || ''}
|
|
375
|
+
onFocus={handleLanguageFocus}
|
|
376
|
+
onBlur={onBlur}
|
|
377
|
+
>
|
|
378
|
+
{languages.map((lang: {title: string; value: string}) => (
|
|
379
|
+
<option key={lang.value} value={lang.value}>
|
|
380
|
+
{lang.title}
|
|
381
|
+
</option>
|
|
382
|
+
))}
|
|
383
|
+
</Select>
|
|
384
|
+
</FormField>
|
|
385
|
+
</ChangeIndicatorProvider>
|
|
386
|
+
{type.options?.withFilename && (
|
|
387
|
+
<ChangeIndicatorProvider
|
|
388
|
+
path={PATH_FILENAME}
|
|
389
|
+
focusPath={focusPath}
|
|
390
|
+
value={value?.filename}
|
|
391
|
+
compareValue={filenameCompareValue}
|
|
392
|
+
>
|
|
393
|
+
<FormField
|
|
394
|
+
label={filenameField?.type.title || 'Filename'}
|
|
395
|
+
level={level + 1}
|
|
396
|
+
__unstable_presence={filenamePresence}
|
|
397
|
+
>
|
|
398
|
+
<TextInput
|
|
399
|
+
name="filename"
|
|
400
|
+
value={value?.filename || ''}
|
|
401
|
+
placeholder={filenameField?.type.placeholder}
|
|
402
|
+
onChange={handleFilenameChange}
|
|
403
|
+
onFocus={handleFilenameFocus}
|
|
404
|
+
onBlur={onBlur}
|
|
405
|
+
/>
|
|
406
|
+
</FormField>
|
|
407
|
+
</ChangeIndicatorProvider>
|
|
408
|
+
)}
|
|
409
|
+
<ChangeIndicatorProvider
|
|
410
|
+
path={PATH_CODE}
|
|
411
|
+
focusPath={focusPath}
|
|
412
|
+
value={value?.code}
|
|
413
|
+
compareValue={codeCompareValue}
|
|
414
|
+
>
|
|
415
|
+
<FormField label="Code" level={level + 1} __unstable_presence={codePresence}>
|
|
416
|
+
{renderEditor()}
|
|
417
|
+
</FormField>
|
|
418
|
+
</ChangeIndicatorProvider>
|
|
419
|
+
</Stack>
|
|
420
|
+
</FormFieldSet>
|
|
421
|
+
)
|
|
422
|
+
}
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
CodeInput.displayName = 'CodeInput'
|
|
426
|
+
|
|
427
|
+
export default CodeInput
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import React, {useCallback, useEffect, useRef} from 'react'
|
|
2
|
+
import AceEditor from 'react-ace'
|
|
3
|
+
import styled from 'styled-components'
|
|
4
|
+
import {Box} from '@sanity/ui'
|
|
5
|
+
import {ACE_EDITOR_PROPS, ACE_SET_OPTIONS} from './config'
|
|
6
|
+
import createHighlightMarkers from './createHighlightMarkers'
|
|
7
|
+
import {CodeInputType, CodeInputValue} from './types'
|
|
8
|
+
/* eslint-disable-next-line import/no-unassigned-import */
|
|
9
|
+
import './editorSupport'
|
|
10
|
+
|
|
11
|
+
const PreviewContainer = styled(Box)`
|
|
12
|
+
position: relative;
|
|
13
|
+
`
|
|
14
|
+
|
|
15
|
+
const PreviewInner = styled(Box)`
|
|
16
|
+
background-color: #272822;
|
|
17
|
+
|
|
18
|
+
.ace_editor {
|
|
19
|
+
box-sizing: border-box;
|
|
20
|
+
cursor: default;
|
|
21
|
+
pointer-events: none;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.ace_content {
|
|
25
|
+
box-sizing: border-box;
|
|
26
|
+
overflow: hidden;
|
|
27
|
+
}
|
|
28
|
+
`
|
|
29
|
+
|
|
30
|
+
export interface PreviewCodeProps {
|
|
31
|
+
type?: CodeInputType
|
|
32
|
+
value?: CodeInputValue
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default function PreviewCode(props: PreviewCodeProps) {
|
|
36
|
+
const aceEditorRef = useRef<any>()
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (!aceEditorRef?.current) return
|
|
40
|
+
|
|
41
|
+
const editor = aceEditorRef.current?.editor
|
|
42
|
+
|
|
43
|
+
if (editor) {
|
|
44
|
+
// Avoid cursor and focus tracking by Ace
|
|
45
|
+
editor.renderer.$cursorLayer.element.style.opacity = 0
|
|
46
|
+
editor.textInput.getElement().disabled = true
|
|
47
|
+
}
|
|
48
|
+
}, [])
|
|
49
|
+
|
|
50
|
+
const handleEditorChange = useCallback(() => {
|
|
51
|
+
// do nothing when the editor changes
|
|
52
|
+
}, [])
|
|
53
|
+
|
|
54
|
+
const {value, type} = props
|
|
55
|
+
const fixedLanguage = type?.options?.language
|
|
56
|
+
|
|
57
|
+
const mode = value?.language || fixedLanguage || 'text'
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<PreviewContainer>
|
|
61
|
+
<PreviewInner padding={4}>
|
|
62
|
+
<AceEditor
|
|
63
|
+
ref={aceEditorRef}
|
|
64
|
+
focus={false}
|
|
65
|
+
mode={mode}
|
|
66
|
+
theme="monokai"
|
|
67
|
+
width="100%"
|
|
68
|
+
onChange={handleEditorChange}
|
|
69
|
+
maxLines={200}
|
|
70
|
+
readOnly
|
|
71
|
+
wrapEnabled
|
|
72
|
+
showPrintMargin={false}
|
|
73
|
+
highlightActiveLine={false}
|
|
74
|
+
cursorStart={-1}
|
|
75
|
+
value={(value && value.code) || ''}
|
|
76
|
+
markers={
|
|
77
|
+
value && value.highlightedLines
|
|
78
|
+
? createHighlightMarkers(value.highlightedLines)
|
|
79
|
+
: undefined
|
|
80
|
+
}
|
|
81
|
+
tabSize={2}
|
|
82
|
+
showGutter={false}
|
|
83
|
+
setOptions={ACE_SET_OPTIONS}
|
|
84
|
+
editorProps={ACE_EDITOR_PROPS}
|
|
85
|
+
/>
|
|
86
|
+
</PreviewInner>
|
|
87
|
+
</PreviewContainer>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import {createSchema} from '@sanity/base/_unstable'
|
|
2
|
+
import {ValidationMarker, Path} from '@sanity/types'
|
|
3
|
+
import {Box} from '@sanity/ui'
|
|
4
|
+
import {useAction} from '@sanity/ui-workshop'
|
|
5
|
+
import React, {useState} from 'react'
|
|
6
|
+
import CodeInput, {CodeSchemaType} from '../CodeInput'
|
|
7
|
+
import typeDef from '../schema'
|
|
8
|
+
|
|
9
|
+
const schema = createSchema({name: 'dev', types: [typeDef]})
|
|
10
|
+
const type = schema.get('code') as CodeSchemaType
|
|
11
|
+
|
|
12
|
+
export default function DevStory() {
|
|
13
|
+
const [focusPath] = useState<Path>([])
|
|
14
|
+
const [validation] = useState<ValidationMarker[]>([])
|
|
15
|
+
const onBlur = useAction('onBlur')
|
|
16
|
+
const onChange = useAction('onChange')
|
|
17
|
+
const onFocus = useAction('onFocus')
|
|
18
|
+
const [presence] = useState<any[]>([])
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<Box padding={4}>
|
|
22
|
+
<CodeInput
|
|
23
|
+
focusPath={focusPath}
|
|
24
|
+
level={0}
|
|
25
|
+
validation={validation}
|
|
26
|
+
onBlur={onBlur}
|
|
27
|
+
onChange={onChange}
|
|
28
|
+
onFocus={onFocus}
|
|
29
|
+
presence={presence}
|
|
30
|
+
type={type}
|
|
31
|
+
/>
|
|
32
|
+
</Box>
|
|
33
|
+
)
|
|
34
|
+
}
|