@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.
Files changed (113) hide show
  1. package/CodeInput.js +7 -0
  2. package/deprecatedSchema.js +7 -0
  3. package/lib/CodeInput.cjs +42 -0
  4. package/lib/CodeInput.cjs.map +1 -0
  5. package/lib/CodeInput.js +36 -333
  6. package/lib/CodeInput.js.map +1 -0
  7. package/lib/_CodeInput-1b58302e.js +893 -0
  8. package/lib/_CodeInput-1b58302e.js.map +1 -0
  9. package/lib/_CodeInput-95db5e9a.cjs +923 -0
  10. package/lib/_CodeInput-95db5e9a.cjs.map +1 -0
  11. package/lib/_reExport.js +19 -0
  12. package/lib/deprecatedSchema.cjs +23 -0
  13. package/lib/deprecatedSchema.cjs.map +1 -0
  14. package/lib/deprecatedSchema.js +19 -24
  15. package/lib/deprecatedSchema.js.map +1 -0
  16. package/lib/dts/src/CodeInput.d.ts +20 -0
  17. package/lib/dts/src/CodeInput.d.ts.map +1 -0
  18. package/lib/dts/src/CodeInput.js +201 -0
  19. package/lib/dts/src/CodeInput.js.map +1 -0
  20. package/{dist/dts → lib/dts/src}/PreviewCode.d.ts +8 -8
  21. package/lib/dts/src/PreviewCode.d.ts.map +1 -0
  22. package/lib/dts/src/PreviewCode.js +50 -0
  23. package/lib/dts/src/PreviewCode.js.map +1 -0
  24. package/lib/dts/src/__workshop__/dev.d.ts +3 -0
  25. package/lib/dts/src/__workshop__/dev.d.ts.map +1 -0
  26. package/lib/dts/src/__workshop__/dev.js +19 -0
  27. package/lib/dts/src/__workshop__/dev.js.map +1 -0
  28. package/lib/dts/src/__workshop__/index.d.ts +3 -0
  29. package/lib/dts/src/__workshop__/index.d.ts.map +1 -0
  30. package/lib/dts/src/__workshop__/index.js +10 -0
  31. package/lib/dts/src/__workshop__/index.js.map +1 -0
  32. package/{dist/dts → lib/dts/src}/config.d.ts +15 -15
  33. package/lib/dts/src/config.d.ts.map +1 -0
  34. package/lib/dts/src/config.js +38 -0
  35. package/lib/dts/src/config.js.map +1 -0
  36. package/{dist/dts → lib/dts/src}/createHighlightMarkers.d.ts +3 -3
  37. package/lib/dts/src/createHighlightMarkers.d.ts.map +1 -0
  38. package/lib/dts/src/createHighlightMarkers.js +22 -0
  39. package/lib/dts/src/createHighlightMarkers.js.map +1 -0
  40. package/{dist/dts → lib/dts/src}/deprecatedSchema.d.ts +11 -11
  41. package/lib/dts/src/deprecatedSchema.d.ts.map +1 -0
  42. package/lib/dts/src/deprecatedSchema.js +19 -0
  43. package/lib/dts/src/deprecatedSchema.js.map +1 -0
  44. package/{dist/dts → lib/dts/src}/editorSupport.d.ts +27 -27
  45. package/lib/dts/src/editorSupport.d.ts.map +1 -0
  46. package/lib/dts/src/editorSupport.js +32 -0
  47. package/lib/dts/src/editorSupport.js.map +1 -0
  48. package/{dist/dts → lib/dts/src}/getMedia.d.ts +2 -2
  49. package/lib/dts/src/getMedia.d.ts.map +1 -0
  50. package/lib/dts/src/getMedia.js +35 -0
  51. package/lib/dts/src/getMedia.js.map +1 -0
  52. package/lib/dts/src/groq.d.ts +2 -0
  53. package/lib/dts/src/groq.d.ts.map +1 -0
  54. package/lib/dts/src/groq.js +612 -0
  55. package/lib/dts/src/groq.js.map +1 -0
  56. package/lib/dts/src/index.d.ts +4 -0
  57. package/lib/dts/src/index.d.ts.map +1 -0
  58. package/lib/dts/src/index.js +7 -0
  59. package/lib/dts/src/index.js.map +1 -0
  60. package/lib/dts/src/schema.d.ts +46 -0
  61. package/lib/dts/src/schema.d.ts.map +1 -0
  62. package/lib/dts/src/schema.js +59 -0
  63. package/lib/dts/src/schema.js.map +1 -0
  64. package/{dist/dts → lib/dts/src}/types.d.ts +28 -28
  65. package/lib/dts/src/types.d.ts.map +1 -0
  66. package/lib/dts/src/types.js +2 -0
  67. package/lib/dts/src/types.js.map +1 -0
  68. package/lib/dts/tsconfig.tsbuildinfo +1 -0
  69. package/lib/index.cjs +48 -0
  70. package/lib/index.cjs.map +1 -0
  71. package/lib/index.js +46 -0
  72. package/lib/index.js.map +1 -0
  73. package/lib/schema.cjs +177 -0
  74. package/lib/schema.cjs.map +1 -0
  75. package/lib/schema.js +162 -62
  76. package/lib/schema.js.map +1 -0
  77. package/package.json +63 -11
  78. package/schema.js +7 -0
  79. package/src/CodeInput.tsx +427 -0
  80. package/src/PreviewCode.tsx +89 -0
  81. package/src/__workshop__/dev.tsx +34 -0
  82. package/src/__workshop__/index.ts +10 -0
  83. package/src/config.ts +45 -0
  84. package/src/createHighlightMarkers.ts +24 -0
  85. package/src/deprecatedSchema.ts +19 -0
  86. package/src/editorSupport.ts +33 -0
  87. package/src/getMedia.tsx +95 -0
  88. package/src/groq.ts +630 -0
  89. package/src/index.ts +11 -0
  90. package/src/schema.tsx +69 -0
  91. package/src/types.ts +26 -0
  92. package/dist/dts/CodeInput.d.ts +0 -23
  93. package/dist/dts/CodeInput.d.ts.map +0 -1
  94. package/dist/dts/PreviewCode.d.ts.map +0 -1
  95. package/dist/dts/config.d.ts.map +0 -1
  96. package/dist/dts/createHighlightMarkers.d.ts.map +0 -1
  97. package/dist/dts/deprecatedSchema.d.ts.map +0 -1
  98. package/dist/dts/editorSupport.d.ts.map +0 -1
  99. package/dist/dts/getMedia.d.ts.map +0 -1
  100. package/dist/dts/groq.d.ts +0 -376
  101. package/dist/dts/groq.d.ts.map +0 -1
  102. package/dist/dts/schema.d.ts +0 -44
  103. package/dist/dts/schema.d.ts.map +0 -1
  104. package/dist/dts/types.d.ts.map +0 -1
  105. package/lib/@types/css.d.js +0 -1
  106. package/lib/PreviewCode.js +0 -79
  107. package/lib/config.js +0 -103
  108. package/lib/createHighlightMarkers.js +0 -31
  109. package/lib/editorSupport.js +0 -55
  110. package/lib/getMedia.js +0 -110
  111. package/lib/groq.js +0 -414
  112. package/lib/types.js +0 -5
  113. 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",
3
+ "version": "2.29.4-purple-unicorn.509+183505a0b3",
4
4
  "description": "Ace editor for editing code",
5
- "main": "./lib/index.js",
6
- "types": "./dist/dts",
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
- "clean": "rimraf lib dest"
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/base": "2.29.3",
25
- "@sanity/icons": "^1.2.1",
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
- "rimraf": "^2.7.1"
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": "01422a8d4b1eae4b1491dbaf5da9bafc7823c4ea"
101
+ "gitHead": "183505a0b3ea400181bff4f4370c146374b58068"
50
102
  }
package/schema.js ADDED
@@ -0,0 +1,7 @@
1
+ /* eslint-disable prettier/prettier, strict */
2
+ 'use strict'
3
+
4
+ // AUTO-GENERATED – DO NOT EDIT
5
+ // This is a legacy package export
6
+ // prettier-ignore
7
+ require('./lib/_reExport')(module.exports, require('./lib/schema.cjs'))
@@ -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
+ }
@@ -0,0 +1,10 @@
1
+ import {defineScope} from '@sanity/ui-workshop'
2
+ import {lazy} from 'react'
3
+
4
+ export default defineScope('code-input', '@sanity/code-input', [
5
+ {
6
+ name: 'dev',
7
+ title: 'Dev',
8
+ component: lazy(() => import('./dev')),
9
+ },
10
+ ])