@sanity/code-input 3.0.1 → 4.1.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.
Files changed (52) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +140 -64
  3. package/lib/_chunks/CodeMirrorProxy-3836f097.js +619 -0
  4. package/lib/_chunks/CodeMirrorProxy-3836f097.js.map +1 -0
  5. package/lib/_chunks/CodeMirrorProxy-e83d4d37.js +611 -0
  6. package/lib/_chunks/CodeMirrorProxy-e83d4d37.js.map +1 -0
  7. package/lib/_chunks/index-17e68aff.js +563 -0
  8. package/lib/_chunks/index-17e68aff.js.map +1 -0
  9. package/lib/_chunks/index-9a4cb814.js +549 -0
  10. package/lib/_chunks/index-9a4cb814.js.map +1 -0
  11. package/lib/{src/index.d.ts → index.d.ts} +18 -10
  12. package/lib/index.esm.js +1 -1
  13. package/lib/index.esm.js.map +1 -1
  14. package/lib/index.js +10 -1
  15. package/lib/index.js.map +1 -1
  16. package/package.json +53 -27
  17. package/src/CodeInput.tsx +72 -272
  18. package/src/LanguageField.tsx +33 -0
  19. package/src/LanguageInput.tsx +32 -0
  20. package/src/PreviewCode.tsx +40 -68
  21. package/src/__workshop__/index.ts +22 -0
  22. package/src/__workshop__/lazy.tsx +54 -0
  23. package/src/__workshop__/preview.tsx +24 -0
  24. package/src/__workshop__/props.tsx +24 -0
  25. package/src/codemirror/CodeMirrorProxy.tsx +157 -0
  26. package/src/codemirror/CodeModeContext.tsx +4 -0
  27. package/src/codemirror/defaultCodeModes.ts +109 -0
  28. package/src/codemirror/extensions/highlightLineExtension.ts +164 -0
  29. package/src/codemirror/extensions/theme.ts +61 -0
  30. package/src/codemirror/extensions/useCodeMirrorTheme.ts +63 -0
  31. package/src/codemirror/extensions/useFontSize.ts +24 -0
  32. package/src/codemirror/useCodeMirror-client.test.tsx +49 -0
  33. package/src/{ace-editor/AceEditor-server.test.tsx → codemirror/useCodeMirror-server.test.tsx} +3 -4
  34. package/src/codemirror/useCodeMirror.tsx +12 -0
  35. package/src/codemirror/useLanguageMode.tsx +52 -0
  36. package/src/config.ts +1 -13
  37. package/src/getMedia.tsx +0 -2
  38. package/src/index.ts +4 -11
  39. package/src/plugin.tsx +39 -0
  40. package/src/schema.tsx +3 -7
  41. package/src/types.ts +19 -3
  42. package/src/ui/focusRingStyle.ts +27 -0
  43. package/src/useFieldMember.ts +16 -0
  44. package/lib/_chunks/editorSupport-895caf32.esm.js +0 -2
  45. package/lib/_chunks/editorSupport-895caf32.esm.js.map +0 -1
  46. package/lib/_chunks/editorSupport-bda3d360.js +0 -2
  47. package/lib/_chunks/editorSupport-bda3d360.js.map +0 -1
  48. package/src/ace-editor/AceEditor-client.test.tsx +0 -37
  49. package/src/ace-editor/AceEditorLazy.tsx +0 -19
  50. package/src/ace-editor/editorSupport.ts +0 -34
  51. package/src/ace-editor/groq.ts +0 -630
  52. package/src/createHighlightMarkers.ts +0 -24
package/src/CodeInput.tsx CHANGED
@@ -1,102 +1,57 @@
1
- /* eslint-disable react/jsx-handler-names */
2
- import React, {Suspense, useCallback, useEffect, useImperativeHandle, useMemo, useRef} from 'react'
3
- import {
4
- InputProps,
5
- ObjectSchemaType,
6
- StringInputProps,
7
- useColorScheme,
8
- FieldMember,
9
- MemberField,
10
- ObjectInputProps,
11
- set,
12
- setIfMissing,
13
- unset,
14
- RenderInputCallback,
15
- } from 'sanity'
16
- import {Card, Select, Stack, ThemeColorSchemeKey} from '@sanity/ui'
17
- import styled from 'styled-components'
18
- import createHighlightMarkers, {highlightMarkersCSS} from './createHighlightMarkers'
19
- import {CodeInputLanguage, CodeInputValue} from './types'
20
- import {
21
- ACE_EDITOR_PROPS,
22
- ACE_SET_OPTIONS,
23
- DEFAULT_DARK_THEME,
24
- DEFAULT_THEME,
25
- LANGUAGE_ALIASES,
26
- PATH_CODE,
27
- SUPPORTED_LANGUAGES,
28
- SUPPORTED_THEMES,
29
- } from './config'
30
- import {useAceEditor} from './ace-editor/AceEditorLazy'
1
+ import {Suspense, useCallback} from 'react'
2
+ import {MemberField, ObjectInputProps, RenderInputCallback, set, setIfMissing, unset} from 'sanity'
3
+ import {Box, Card, Stack, Text} from '@sanity/ui'
4
+ import styled, {css} from 'styled-components'
5
+ import {LanguageField} from './LanguageField'
6
+ import {useCodeMirror} from './codemirror/useCodeMirror'
7
+ import {useLanguageMode} from './codemirror/useLanguageMode'
8
+ import {PATH_CODE} from './config'
9
+ import {CodeInputValue, CodeSchemaType} from './types'
10
+ import {useFieldMember} from './useFieldMember'
11
+ import {focusRingBorderStyle, focusRingStyle} from './ui/focusRingStyle'
31
12
 
32
13
  export type {CodeInputLanguage, CodeInputValue} from './types'
33
14
 
34
- const EditorContainer = styled(Card)`
35
- position: relative;
36
- box-sizing: border-box;
37
- overflow: hidden;
38
- z-index: 0;
39
-
40
- .ace_editor {
41
- font-family: ${({theme}) => theme.sanity.fonts.code.family};
42
- font-size: ${({theme}) => theme.sanity.fonts.code.sizes[1]};
43
- line-height: inherit;
44
- }
45
-
46
- ${highlightMarkersCSS}
47
-
48
- &:not([disabled]):not([readonly]) {
49
- &:focus,
50
- &:focus-within {
51
- box-shadow: 0 0 0 2px ${({theme}) => theme.sanity.color.base.focusRing};
52
- background-color: ${({theme}) => theme.sanity.color.base.bg};
53
- border-color: ${({theme}) => theme.sanity.color.base.focusRing};
54
- }
55
- }
56
- `
57
-
58
- /**
59
- * @public
60
- */
61
- export 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 type CodeSchemaType = Omit<ObjectSchemaType, 'options'> & {
73
- options?: CodeOptions
74
- }
75
-
76
15
  /**
77
16
  * @public
78
17
  */
79
- export type CodeInputProps = ObjectInputProps<CodeInputValue, CodeSchemaType> & {
80
- /** @internal */
81
- colorScheme?: ThemeColorSchemeKey
82
- }
18
+ export interface CodeInputProps extends ObjectInputProps<CodeInputValue, CodeSchemaType> {}
19
+
20
+ const EditorContainer = styled(Card)(({theme}) => {
21
+ const {focusRing, input} = theme.sanity
22
+ const base = theme.sanity.color.base
23
+ const color = theme.sanity.color.input
24
+ const border = {
25
+ color: color.default.enabled.border,
26
+ width: input.border.width,
27
+ }
83
28
 
84
- // Returns a string with the mode name if supported (because aliases), otherwise false
85
- function isSupportedLanguage(mode: string) {
86
- const alias = LANGUAGE_ALIASES[mode]
29
+ return css`
30
+ --input-box-shadow: ${focusRingBorderStyle(border)};
87
31
 
88
- if (alias) {
89
- return alias
90
- }
32
+ box-shadow: var(--input-box-shadow);
33
+ height: 250px;
34
+ min-height: 80px;
35
+ overflow-y: auto;
36
+ position: relative;
37
+ resize: vertical;
38
+ z-index: 0;
91
39
 
92
- const isSupported = SUPPORTED_LANGUAGES.find((lang) => lang.value === mode)
93
- if (isSupported) {
94
- return mode
95
- }
40
+ & > .cm-theme {
41
+ height: 100%;
42
+ }
96
43
 
97
- return false
98
- }
44
+ &:focus-within {
45
+ --input-box-shadow: ${focusRingStyle({
46
+ base,
47
+ border,
48
+ focusRing,
49
+ })};
50
+ }
51
+ `
52
+ })
99
53
 
54
+ /** @public */
100
55
  export function CodeInput(props: CodeInputProps) {
101
56
  const {
102
57
  members,
@@ -112,135 +67,19 @@ export function CodeInput(props: CodeInputProps) {
112
67
  onPathFocus,
113
68
  } = props
114
69
 
115
- const aceEditorRef = useRef<any>()
116
-
117
- const fieldMembers = useMemo(
118
- () => members.filter((member) => member.kind === 'field') as FieldMember[],
119
- [members]
120
- )
121
-
122
- const languageFieldMember = fieldMembers.find((member) => member.name === 'language')
123
- const filenameMember = fieldMembers.find((member) => member.name === 'filename')
124
- const codeFieldMember = fieldMembers.find((member) => member.name === 'code')
125
-
126
- useImperativeHandle(elementProps.ref, () => ({
127
- focus: () => {
128
- aceEditorRef?.current?.editor?.focus()
129
- },
130
- }))
70
+ const languageFieldMember = useFieldMember(members, 'language')
71
+ const filenameMember = useFieldMember(members, 'filename')
72
+ const codeFieldMember = useFieldMember(members, 'code')
131
73
 
132
74
  const handleCodeFocus = useCallback(() => {
133
75
  onPathFocus(PATH_CODE)
134
76
  }, [onPathFocus])
135
77
 
136
- const {scheme} = useColorScheme()
137
-
138
- const theme = useMemo(() => {
139
- const isLight = scheme === 'light'
140
- const preferredTheme = isLight ? type.options?.theme : type.options?.darkTheme
141
- const defaultTheme = isLight ? DEFAULT_THEME : DEFAULT_DARK_THEME
142
- return preferredTheme && SUPPORTED_THEMES.find((t) => t === preferredTheme)
143
- ? preferredTheme
144
- : defaultTheme
145
- }, [type, scheme])
146
-
147
- const handleToggleSelectLine = useCallback(
148
- (lineNumber: number) => {
149
- const editorSession = aceEditorRef.current?.editor?.getSession()
150
- const backgroundMarkers = editorSession?.getMarkers(true)
151
- const currentHighlightedLines = Object.keys(backgroundMarkers)
152
- .filter((key) => backgroundMarkers[key].type === 'screenLine')
153
- .map((key) => backgroundMarkers[key].range.start.row)
154
- const currentIndex = currentHighlightedLines.indexOf(lineNumber)
155
- if (currentIndex > -1) {
156
- // toggle remove
157
- currentHighlightedLines.splice(currentIndex, 1)
158
- } else {
159
- // toggle add
160
- currentHighlightedLines.push(lineNumber)
161
- currentHighlightedLines.sort()
162
- }
163
- onChange(
164
- set(
165
- currentHighlightedLines.map(
166
- (line) =>
167
- // ace starts at line (row) 0, but we store it starting at line 1
168
- line + 1
169
- ),
170
- ['highlightedLines']
171
- )
172
- )
173
- },
174
- [aceEditorRef, onChange]
175
- )
176
-
177
- const handleGutterMouseDown = useCallback(
178
- (event: any) => {
179
- const target = event.domEvent.target
180
- if (target.classList.contains('ace_gutter-cell')) {
181
- const row = event.getDocumentPosition().row
182
- handleToggleSelectLine(row)
183
- }
184
- },
185
- [handleToggleSelectLine]
186
- )
187
-
188
- useEffect(() => {
189
- const editor = aceEditorRef?.current?.editor
190
- return () => {
191
- editor?.session?.removeListener('guttermousedown', handleGutterMouseDown)
192
- }
193
- }, [aceEditorRef, handleGutterMouseDown])
194
-
195
- const handleEditorLoad = useCallback(
196
- (editor: any) => {
197
- editor?.on('guttermousedown', handleGutterMouseDown)
198
- },
199
- [handleGutterMouseDown]
78
+ const onHighlightChange = useCallback(
79
+ (lines: number[]) => onChange(set(lines, ['highlightedLines'])),
80
+ [onChange]
200
81
  )
201
82
 
202
- const getLanguageAlternatives = useCallback((): {
203
- title: string
204
- value: string
205
- mode?: string
206
- }[] => {
207
- const languageAlternatives = type.options?.languageAlternatives
208
- if (!languageAlternatives) {
209
- return SUPPORTED_LANGUAGES
210
- }
211
-
212
- if (!Array.isArray(languageAlternatives)) {
213
- throw new Error(
214
- `'options.languageAlternatives' should be an array, got ${typeof languageAlternatives}`
215
- )
216
- }
217
-
218
- return languageAlternatives.reduce((acc: CodeInputLanguage[], {title, value: val, mode}) => {
219
- const alias = LANGUAGE_ALIASES[val]
220
- if (alias) {
221
- // eslint-disable-next-line no-console
222
- console.warn(
223
- `'options.languageAlternatives' lists a language with value "%s", which is an alias of "%s" - please replace the value to read "%s"`,
224
- val,
225
- alias,
226
- alias
227
- )
228
-
229
- return acc.concat({title, value: alias, mode: mode})
230
- }
231
-
232
- if (!mode && !SUPPORTED_LANGUAGES.find((lang) => lang.value === val)) {
233
- // eslint-disable-next-line no-console
234
- console.warn(
235
- `'options.languageAlternatives' lists a language which is not supported: "%s", syntax highlighting will be disabled.`,
236
- val
237
- )
238
- }
239
-
240
- return acc.concat({title, value: val, mode})
241
- }, [])
242
- }, [type])
243
-
244
83
  const handleCodeChange = useCallback(
245
84
  (code: string) => {
246
85
  const path = PATH_CODE
@@ -253,69 +92,29 @@ export function CodeInput(props: CodeInputProps) {
253
92
  },
254
93
  [onChange, type]
255
94
  )
95
+ const {languages, language, languageMode} = useLanguageMode(props.schemaType, props.value)
256
96
 
257
- const languages = getLanguageAlternatives().slice()
258
-
259
- const fixedLanguage = type.options?.language
260
-
261
- const language = value?.language || fixedLanguage
262
-
263
- // the language config from the schema
264
- const configured = languages.find((entry) => entry.value === language)
265
-
266
- // is the language officially supported (e.g. we import the mode by default)
267
- const supported = language && isSupportedLanguage(language)
268
-
269
- const mode = configured?.mode || (supported ? language : 'text')
270
-
271
- const renderLanguageInput = useCallback(
272
- (inputProps: Omit<InputProps, 'renderDefault'>) => {
273
- return (
274
- <Select
275
- {...(inputProps as StringInputProps)}
276
- onChange={(e) => {
277
- const newValue = e.currentTarget.value
278
- inputProps.onChange(newValue ? set(newValue) : unset())
279
- }}
280
- >
281
- {languages.map((lang: {title: string; value: string}) => (
282
- <option key={lang.value} value={lang.value}>
283
- {lang.title}
284
- </option>
285
- ))}
286
- </Select>
287
- )
288
- },
289
- [languages]
290
- )
291
-
292
- const AceEditor = useAceEditor()
97
+ const CodeMirror = useCodeMirror()
293
98
 
294
99
  const renderCodeInput: RenderInputCallback = useCallback(
295
100
  (inputProps) => {
296
101
  return (
297
- <EditorContainer radius={1} shadow={1} readOnly={readOnly}>
298
- {AceEditor && (
299
- <Suspense fallback={<div>Loading code editor...</div>}>
300
- <AceEditor
301
- ref={aceEditorRef}
302
- mode={mode}
303
- theme={theme}
304
- width="100%"
102
+ <EditorContainer border overflow="hidden" radius={1} sizing="border" readOnly={readOnly}>
103
+ {CodeMirror && (
104
+ <Suspense
105
+ fallback={
106
+ <Box padding={3}>
107
+ <Text>Loading code editor...</Text>
108
+ </Box>
109
+ }
110
+ >
111
+ <CodeMirror
112
+ languageMode={languageMode}
305
113
  onChange={handleCodeChange}
306
- name={inputProps.id}
307
114
  value={inputProps.value as string}
308
- markers={
309
- value && value.highlightedLines
310
- ? createHighlightMarkers(value.highlightedLines)
311
- : undefined
312
- }
313
- onLoad={handleEditorLoad}
115
+ highlightLines={value?.highlightedLines}
116
+ onHighlightChange={onHighlightChange}
314
117
  readOnly={readOnly}
315
- tabSize={2}
316
- wrapEnabled
317
- setOptions={ACE_SET_OPTIONS}
318
- editorProps={ACE_EDITOR_PROPS}
319
118
  onFocus={handleCodeFocus}
320
119
  onBlur={elementProps.onBlur}
321
120
  />
@@ -325,12 +124,11 @@ export function CodeInput(props: CodeInputProps) {
325
124
  )
326
125
  },
327
126
  [
328
- AceEditor,
329
- theme,
127
+ CodeMirror,
330
128
  handleCodeChange,
331
129
  handleCodeFocus,
332
- handleEditorLoad,
333
- mode,
130
+ onHighlightChange,
131
+ languageMode,
334
132
  elementProps.onBlur,
335
133
  readOnly,
336
134
  value,
@@ -340,11 +138,13 @@ export function CodeInput(props: CodeInputProps) {
340
138
  return (
341
139
  <Stack space={4}>
342
140
  {languageFieldMember && (
343
- <MemberField
141
+ <LanguageField
344
142
  member={languageFieldMember}
345
- renderItem={renderItem}
143
+ language={language}
144
+ languages={languages}
346
145
  renderField={renderField}
347
- renderInput={renderLanguageInput}
146
+ renderItem={renderItem}
147
+ renderInput={renderInput}
348
148
  renderPreview={renderPreview}
349
149
  />
350
150
  )}
@@ -0,0 +1,33 @@
1
+ import {useCallback} from 'react'
2
+ import {FieldMember, InputProps, MemberField, MemberFieldProps, StringInputProps} from 'sanity'
3
+ import {CodeInputLanguage} from './types'
4
+ import {LanguageInput} from './LanguageInput'
5
+
6
+ export function LanguageField(
7
+ props: MemberFieldProps & {member: FieldMember; language: string; languages: CodeInputLanguage[]}
8
+ ) {
9
+ const {member, languages, language, renderItem, renderField, renderPreview} = props
10
+
11
+ const renderInput = useCallback(
12
+ (inputProps: Omit<InputProps, 'renderDefault'>) => {
13
+ return (
14
+ <LanguageInput
15
+ {...(inputProps as StringInputProps)}
16
+ language={language}
17
+ languages={languages}
18
+ />
19
+ )
20
+ },
21
+ [languages, language]
22
+ )
23
+
24
+ return (
25
+ <MemberField
26
+ member={member}
27
+ renderItem={renderItem}
28
+ renderField={renderField}
29
+ renderInput={renderInput}
30
+ renderPreview={renderPreview}
31
+ />
32
+ )
33
+ }
@@ -0,0 +1,32 @@
1
+ import {ChangeEvent, useCallback} from 'react'
2
+ import {StringInputProps, set, unset} from 'sanity'
3
+ import {Select} from '@sanity/ui'
4
+ import {CodeInputLanguage} from './types'
5
+
6
+ export interface LanguageInputProps extends StringInputProps {
7
+ language: string
8
+ languages: CodeInputLanguage[]
9
+ }
10
+
11
+ /** @internal */
12
+ export function LanguageInput(props: LanguageInputProps) {
13
+ const {language, languages, onChange} = props
14
+
15
+ const handleChange = useCallback(
16
+ (e: ChangeEvent<HTMLSelectElement>) => {
17
+ const newValue = e.currentTarget.value
18
+ onChange(newValue ? set(newValue) : unset())
19
+ },
20
+ [onChange]
21
+ )
22
+
23
+ return (
24
+ <Select {...props} value={language} onChange={handleChange}>
25
+ {languages.map((lang: {title: string; value: string}) => (
26
+ <option key={lang.value} value={lang.value}>
27
+ {lang.title}
28
+ </option>
29
+ ))}
30
+ </Select>
31
+ )
32
+ }
@@ -1,31 +1,15 @@
1
- import React, {Suspense, useCallback, useEffect, useRef} from 'react'
1
+ import {Suspense} from 'react'
2
2
  import styled from 'styled-components'
3
- import {Box} from '@sanity/ui'
4
- import {ACE_EDITOR_PROPS, ACE_SET_OPTIONS} from './config'
5
- import createHighlightMarkers from './createHighlightMarkers'
6
- import {CodeInputValue} from './types'
7
- import {useAceEditor} from './ace-editor/AceEditorLazy'
3
+ import {Label, Box, Card, Flex, Text} from '@sanity/ui'
4
+ import {CodeInputValue, CodeSchemaType} from './types'
8
5
  import {PreviewProps} from 'sanity'
6
+ import {useCodeMirror} from './codemirror/useCodeMirror'
7
+ import {useLanguageMode} from './codemirror/useLanguageMode'
9
8
 
10
9
  const PreviewContainer = styled(Box)`
11
10
  position: relative;
12
11
  `
13
12
 
14
- const PreviewInner = styled(Box)`
15
- background-color: #272822;
16
-
17
- .ace_editor {
18
- box-sizing: border-box;
19
- cursor: default;
20
- pointer-events: none;
21
- }
22
-
23
- .ace_content {
24
- box-sizing: border-box;
25
- overflow: hidden;
26
- }
27
- `
28
-
29
13
  /**
30
14
  * @public
31
15
  */
@@ -37,62 +21,50 @@ export interface PreviewCodeProps extends PreviewProps {
37
21
  * @public
38
22
  */
39
23
  export default function PreviewCode(props: PreviewCodeProps) {
40
- const aceEditorRef = useRef<any>()
41
-
42
- useEffect(() => {
43
- if (!aceEditorRef?.current) return
44
-
45
- const editor = aceEditorRef.current?.editor
46
-
47
- if (editor) {
48
- // Avoid cursor and focus tracking by Ace
49
- editor.renderer.$cursorLayer.element.style.opacity = 0
50
- editor.textInput.getElement().disabled = true
51
- }
52
- }, [])
53
-
54
- const handleEditorChange = useCallback(() => {
55
- // do nothing when the editor changes
56
- }, [])
57
-
58
24
  const {selection, schemaType: type} = props
59
- const fixedLanguage = type?.options?.language
60
-
61
- const mode = selection?.language || fixedLanguage || 'text'
25
+ const {languageMode} = useLanguageMode(type as CodeSchemaType, props.selection)
62
26
 
63
- const AceEditor = useAceEditor()
27
+ const CodeMirror = useCodeMirror()
64
28
  return (
65
29
  <PreviewContainer>
66
- <PreviewInner padding={4}>
67
- {AceEditor && (
68
- <Suspense fallback={<div>Loading code preview...</div>}>
69
- <AceEditor
70
- ref={aceEditorRef}
71
- focus={false}
72
- mode={mode}
73
- theme="monokai"
74
- width="100%"
75
- onChange={handleEditorChange}
76
- maxLines={200}
30
+ <Card padding={4}>
31
+ {selection?.filename || selection?.language ? (
32
+ <Card
33
+ paddingBottom={4}
34
+ marginBottom={selection.code ? 4 : 0}
35
+ borderBottom={!!selection.code}
36
+ >
37
+ <Flex align="center" justify="flex-end">
38
+ {selection?.filename ? (
39
+ <Box flex={1}>
40
+ <Text>
41
+ <code>{selection.filename}</code>
42
+ </Text>
43
+ </Box>
44
+ ) : null}
45
+ {selection?.language ? <Label muted>{selection.language}</Label> : null}
46
+ </Flex>
47
+ </Card>
48
+ ) : null}
49
+ {CodeMirror && (
50
+ <Suspense fallback={<Card padding={2}>Loading code preview...</Card>}>
51
+ <CodeMirror
77
52
  readOnly
78
- wrapEnabled
79
- showPrintMargin={false}
80
- highlightActiveLine={false}
81
- cursorStart={-1}
53
+ editable={false}
82
54
  value={selection?.code || ''}
83
- markers={
84
- selection?.highlightedLines
85
- ? createHighlightMarkers(selection.highlightedLines)
86
- : undefined
87
- }
88
- tabSize={2}
89
- showGutter={false}
90
- setOptions={ACE_SET_OPTIONS}
91
- editorProps={ACE_EDITOR_PROPS}
55
+ highlightLines={selection?.highlightedLines || []}
56
+ basicSetup={{
57
+ lineNumbers: false,
58
+ foldGutter: false,
59
+ highlightSelectionMatches: false,
60
+ highlightActiveLineGutter: false,
61
+ highlightActiveLine: false,
62
+ }}
63
+ languageMode={languageMode}
92
64
  />
93
65
  </Suspense>
94
66
  )}
95
- </PreviewInner>
67
+ </Card>
96
68
  </PreviewContainer>
97
69
  )
98
70
  }
@@ -0,0 +1,22 @@
1
+ import {defineScope} from '@sanity/ui-workshop'
2
+ import {lazy} from 'react'
3
+
4
+ export default defineScope({
5
+ stories: [
6
+ {
7
+ name: 'props',
8
+ title: 'Props',
9
+ component: lazy(() => import('./props')),
10
+ },
11
+ {
12
+ name: 'preview',
13
+ title: 'Preview',
14
+ component: lazy(() => import('./preview')),
15
+ },
16
+ {
17
+ name: 'lazy',
18
+ title: 'Lazy',
19
+ component: lazy(() => import('./lazy')),
20
+ },
21
+ ],
22
+ })
@@ -0,0 +1,54 @@
1
+ import {Box, Card, Container, Text} from '@sanity/ui'
2
+ import {Suspense, useState} from 'react'
3
+ import {useCodeMirror} from '../codemirror/useCodeMirror'
4
+ import {SUPPORTED_LANGUAGES} from '../config'
5
+ import {useSelect} from '@sanity/ui-workshop'
6
+ import styled from 'styled-components'
7
+
8
+ const langs = {
9
+ ...Object.fromEntries(SUPPORTED_LANGUAGES.map((l) => [l.title, l.mode ?? l.value])),
10
+ 'Not supported': 'not-supported',
11
+ }
12
+
13
+ const defaultSnippet = `
14
+ <Card border padding={2}>
15
+ {variable}
16
+ </Card>
17
+ `.trim()
18
+
19
+ const Root = styled(Card)`
20
+ & > .cm-theme {
21
+ height: 100%;
22
+ }
23
+ `
24
+
25
+ export default function CodeMirrorStory() {
26
+ const language = useSelect('Language mode', langs, 'tsx')
27
+ const [code, setCode] = useState(defaultSnippet)
28
+ const [highlights, setHighlights] = useState<number[]>([])
29
+ const CodeMirror = useCodeMirror()
30
+
31
+ return (
32
+ <Container padding={4} sizing="border" width={1}>
33
+ <Root border overflow="hidden" radius={3} style={{height: 300}}>
34
+ {CodeMirror && (
35
+ <Suspense
36
+ fallback={
37
+ <Box padding={3}>
38
+ <Text>Loading code editor...</Text>
39
+ </Box>
40
+ }
41
+ >
42
+ <CodeMirror
43
+ value={code}
44
+ onChange={setCode}
45
+ highlightLines={highlights}
46
+ onHighlightChange={setHighlights}
47
+ languageMode={language}
48
+ />
49
+ </Suspense>
50
+ )}
51
+ </Root>
52
+ </Container>
53
+ )
54
+ }