@instructure/ui-source-code-editor 11.6.0 → 11.6.1-snapshot-129
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/CHANGELOG.md +40 -293
- package/es/SourceCodeEditor/{SearchPanel.js → v1/SearchPanel.js} +2 -2
- package/es/SourceCodeEditor/{index.js → v1/index.js} +1 -1
- package/es/SourceCodeEditor/v2/SearchPanel.js +118 -0
- package/es/SourceCodeEditor/v2/customKeybinding.js +100 -0
- package/es/SourceCodeEditor/v2/index.js +535 -0
- package/es/SourceCodeEditor/v2/props.js +28 -0
- package/es/SourceCodeEditor/v2/styles.js +228 -0
- package/es/{index.js → exports/a.js} +1 -1
- package/{src/index.ts → es/exports/b.js} +1 -2
- package/lib/SourceCodeEditor/{SearchPanel.js → v1/SearchPanel.js} +8 -8
- package/lib/SourceCodeEditor/{index.js → v1/index.js} +1 -1
- package/lib/SourceCodeEditor/v2/SearchPanel.js +126 -0
- package/lib/SourceCodeEditor/v2/customKeybinding.js +105 -0
- package/lib/SourceCodeEditor/v2/index.js +541 -0
- package/lib/SourceCodeEditor/v2/props.js +33 -0
- package/lib/SourceCodeEditor/v2/styles.js +234 -0
- package/lib/{index.js → exports/a.js} +2 -2
- package/lib/exports/b.js +12 -0
- package/package.json +42 -20
- package/src/SourceCodeEditor/{SearchPanel.tsx → v1/SearchPanel.tsx} +2 -2
- package/src/SourceCodeEditor/{index.tsx → v1/index.tsx} +1 -1
- package/src/SourceCodeEditor/v2/README.md +799 -0
- package/src/SourceCodeEditor/v2/SearchPanel.tsx +150 -0
- package/src/SourceCodeEditor/v2/customKeybinding.ts +125 -0
- package/src/SourceCodeEditor/v2/index.tsx +685 -0
- package/src/SourceCodeEditor/v2/props.ts +263 -0
- package/src/SourceCodeEditor/v2/styles.ts +239 -0
- package/src/exports/a.ts +25 -0
- package/src/exports/b.ts +25 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/types/SourceCodeEditor/v1/SearchPanel.d.ts.map +1 -0
- package/types/SourceCodeEditor/v1/customKeybinding.d.ts.map +1 -0
- package/types/SourceCodeEditor/v1/index.d.ts.map +1 -0
- package/types/SourceCodeEditor/v1/props.d.ts.map +1 -0
- package/types/SourceCodeEditor/v1/styles.d.ts.map +1 -0
- package/types/SourceCodeEditor/v1/theme.d.ts.map +1 -0
- package/types/SourceCodeEditor/v2/SearchPanel.d.ts +7 -0
- package/types/SourceCodeEditor/v2/SearchPanel.d.ts.map +1 -0
- package/types/SourceCodeEditor/v2/customKeybinding.d.ts +4 -0
- package/types/SourceCodeEditor/v2/customKeybinding.d.ts.map +1 -0
- package/types/SourceCodeEditor/v2/index.d.ts +103 -0
- package/types/SourceCodeEditor/v2/index.d.ts.map +1 -0
- package/types/SourceCodeEditor/v2/props.d.ts +142 -0
- package/types/SourceCodeEditor/v2/props.d.ts.map +1 -0
- package/types/SourceCodeEditor/v2/styles.d.ts +14 -0
- package/types/SourceCodeEditor/v2/styles.d.ts.map +1 -0
- package/types/exports/a.d.ts +3 -0
- package/types/exports/a.d.ts.map +1 -0
- package/types/exports/b.d.ts +3 -0
- package/types/exports/b.d.ts.map +1 -0
- package/types/SourceCodeEditor/SearchPanel.d.ts.map +0 -1
- package/types/SourceCodeEditor/customKeybinding.d.ts.map +0 -1
- package/types/SourceCodeEditor/index.d.ts.map +0 -1
- package/types/SourceCodeEditor/props.d.ts.map +0 -1
- package/types/SourceCodeEditor/styles.d.ts.map +0 -1
- package/types/SourceCodeEditor/theme.d.ts.map +0 -1
- package/types/index.d.ts +0 -3
- package/types/index.d.ts.map +0 -1
- /package/es/SourceCodeEditor/{customKeybinding.js → v1/customKeybinding.js} +0 -0
- /package/es/SourceCodeEditor/{props.js → v1/props.js} +0 -0
- /package/es/SourceCodeEditor/{styles.js → v1/styles.js} +0 -0
- /package/es/SourceCodeEditor/{theme.js → v1/theme.js} +0 -0
- /package/lib/SourceCodeEditor/{customKeybinding.js → v1/customKeybinding.js} +0 -0
- /package/lib/SourceCodeEditor/{props.js → v1/props.js} +0 -0
- /package/lib/SourceCodeEditor/{styles.js → v1/styles.js} +0 -0
- /package/lib/SourceCodeEditor/{theme.js → v1/theme.js} +0 -0
- /package/src/SourceCodeEditor/{README.md → v1/README.md} +0 -0
- /package/src/SourceCodeEditor/{customKeybinding.ts → v1/customKeybinding.ts} +0 -0
- /package/src/SourceCodeEditor/{props.ts → v1/props.ts} +0 -0
- /package/src/SourceCodeEditor/{styles.ts → v1/styles.ts} +0 -0
- /package/src/SourceCodeEditor/{theme.ts → v1/theme.ts} +0 -0
- /package/types/SourceCodeEditor/{SearchPanel.d.ts → v1/SearchPanel.d.ts} +0 -0
- /package/types/SourceCodeEditor/{customKeybinding.d.ts → v1/customKeybinding.d.ts} +0 -0
- /package/types/SourceCodeEditor/{index.d.ts → v1/index.d.ts} +0 -0
- /package/types/SourceCodeEditor/{props.d.ts → v1/props.d.ts} +0 -0
- /package/types/SourceCodeEditor/{styles.d.ts → v1/styles.d.ts} +0 -0
- /package/types/SourceCodeEditor/{theme.d.ts → v1/theme.d.ts} +0 -0
|
@@ -0,0 +1,799 @@
|
|
|
1
|
+
---
|
|
2
|
+
describes: CodeEditor
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
A wrapper around the popular [CodeMirror](https://codemirror.net/) code editor component. CodeMirror provides a text input field with features like line gutters, syntax highlighting, and autocompletion.
|
|
6
|
+
|
|
7
|
+
### Built-in features
|
|
8
|
+
|
|
9
|
+
SourceCodeEditor has a lot of built-in features that makes editing code easier.
|
|
10
|
+
|
|
11
|
+
##### Command keybinding
|
|
12
|
+
|
|
13
|
+
The editor has a lot of handy key bindings for commands like copying and deleting lines, moving lines up and down, selection and indentation, etc. See the keymaps here: [defaultKeymap](https://codemirror.net/docs/ref/#commands.defaultKeymap), [closeBracketsKeymap](https://codemirror.net/docs/ref/#autocomplete.closeBracketsKeymap), [historyKeymap](https://codemirror.net/docs/ref/#commands.historyKeymap), [foldKeymap](https://codemirror.net/docs/ref/#language.foldKeymap), [completionKeymap](https://codemirror.net/docs/ref/#autocomplete.completionKeymap), [lintKeymap](https://codemirror.net/docs/ref/#lint.lintKeymap).
|
|
14
|
+
|
|
15
|
+
##### History
|
|
16
|
+
|
|
17
|
+
The history feature remembers the steps of the code editing and selections, and lets you undo and redo them.
|
|
18
|
+
|
|
19
|
+
##### Cursor and selection
|
|
20
|
+
|
|
21
|
+
Instead of using the browser's native selection and cursor, SourceCodeEditor uses its own system. This allows the editor to display secondary selection ranges, and tends to produce a type of selection more in line with that users expect in a text editor.
|
|
22
|
+
|
|
23
|
+
It also allows **multiple** cursors to be placed (`Cmd/Ctrl` + click), multiple ranges to be selected and edited at the same time.
|
|
24
|
+
|
|
25
|
+
**Rectangular selections:** by default, it will react to left mouse drag with the `Option/Alt` key held down. When such a selection occurs, the text within the rectangle that was dragged over will be selected, as one selection range per line.
|
|
26
|
+
|
|
27
|
+
The editor highlights text that matches the current selection.
|
|
28
|
+
|
|
29
|
+
##### Bracket matching and closing
|
|
30
|
+
|
|
31
|
+
Whenever the cursor is next to a bracket, that bracket and the one it matches are highlighted. Or, when no matching bracket is found, another highlighting style is used to indicate this.
|
|
32
|
+
|
|
33
|
+
When a closeable bracket is typed, its closing bracket is immediately inserted after the cursor.
|
|
34
|
+
|
|
35
|
+
### Language support
|
|
36
|
+
|
|
37
|
+
Setting the correct language adds **syntax highlighting** and other helpful features to the editor, like **code folding**, **auto-indentation**, **syntax-aware selection** and **autocompletion** features.
|
|
38
|
+
|
|
39
|
+
**Note:** In case you need support for additional languages, please contact us on [GitHub](https://github.com/instructure/instructure-ui)!
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
---
|
|
43
|
+
type: example
|
|
44
|
+
---
|
|
45
|
+
const languages = {
|
|
46
|
+
json: `{
|
|
47
|
+
"name": "@instructure/ui-source-code-editor",
|
|
48
|
+
"version": "8.24.2",
|
|
49
|
+
"description": "A UI component library made by Instructure Inc.",
|
|
50
|
+
"author": "Instructure, Inc. Engineering and Product Design",
|
|
51
|
+
"module": "./es/index.js",
|
|
52
|
+
"main": "./lib/index.js",
|
|
53
|
+
"types": "./types/index.d.ts",
|
|
54
|
+
"repository": {
|
|
55
|
+
"type": "git",
|
|
56
|
+
"url": "https://github.com/instructure/instructure-ui.git"
|
|
57
|
+
},
|
|
58
|
+
}`,
|
|
59
|
+
javascript: `const fruit: string = "apple"
|
|
60
|
+
|
|
61
|
+
const re = new RegExp('ab+c')
|
|
62
|
+
|
|
63
|
+
function exampleMethod(props: Props) {
|
|
64
|
+
return props ? props.value : null
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* This is an example
|
|
69
|
+
* @param {Object} props
|
|
70
|
+
*/
|
|
71
|
+
const Example = () => {
|
|
72
|
+
return (
|
|
73
|
+
<View as="div" padding={'large'}>
|
|
74
|
+
<Position
|
|
75
|
+
renderTarget={<GoodComponent />}
|
|
76
|
+
placement='end center'
|
|
77
|
+
offsetX='20px'
|
|
78
|
+
>
|
|
79
|
+
<span style={{ padding: '8px', background: 'white' }}>
|
|
80
|
+
Positioned content
|
|
81
|
+
</span>
|
|
82
|
+
</Position>
|
|
83
|
+
</View>
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
render(<Example />)`,
|
|
88
|
+
|
|
89
|
+
html: `<!DOCTYPE html>
|
|
90
|
+
<html lang="en">
|
|
91
|
+
<head>
|
|
92
|
+
<meta charset="UTF-8" />
|
|
93
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
94
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
95
|
+
<title>Example app</title>
|
|
96
|
+
</head>
|
|
97
|
+
<body>
|
|
98
|
+
<div id="app">
|
|
99
|
+
<button onclick="myFunction()">Click me</button>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<script src="script.js"></script>
|
|
103
|
+
</body>
|
|
104
|
+
</html>`,
|
|
105
|
+
css: `a {
|
|
106
|
+
text-decoration: none;
|
|
107
|
+
|
|
108
|
+
&:hover { text-decoration: underline; }
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
a:link, a:visited, a:hover, a:active {
|
|
112
|
+
background-color: green;
|
|
113
|
+
color: white;
|
|
114
|
+
padding: 10px 25px;
|
|
115
|
+
text-align: center;
|
|
116
|
+
text-decoration: none;
|
|
117
|
+
display: inline-block;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.centertext { text-align: center; }
|
|
121
|
+
|
|
122
|
+
img { opacity: 0.5; filter: alpha(opacity=50); }`,
|
|
123
|
+
markdown: `#### The quarterly results look great!
|
|
124
|
+
|
|
125
|
+
> - Revenue was off the chart.
|
|
126
|
+
> - Profits were higher than ever.
|
|
127
|
+
|
|
128
|
+
*Everything* is going according to **plan**.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
type: example
|
|
132
|
+
---`,
|
|
133
|
+
shell: `#!/bin/bash
|
|
134
|
+
|
|
135
|
+
# example of using arguments to a script
|
|
136
|
+
echo "My first name is $1"
|
|
137
|
+
echo "My surname is $2"
|
|
138
|
+
echo "Total number of arguments is $#"
|
|
139
|
+
|
|
140
|
+
________________________________________
|
|
141
|
+
|
|
142
|
+
$ chmod a+x name.sh
|
|
143
|
+
$ ./name.sh Hans-Wolfgang Loidl
|
|
144
|
+
My first name is Hans-Wolfgang
|
|
145
|
+
My surname is Loidl
|
|
146
|
+
Total number of arguments is 2`,
|
|
147
|
+
yml: `---
|
|
148
|
+
doe: "a deer, a female deer"
|
|
149
|
+
ray: "a drop of golden sun"
|
|
150
|
+
pi: 3.14159
|
|
151
|
+
xmas: true
|
|
152
|
+
french-hens: 3
|
|
153
|
+
calling-birds:
|
|
154
|
+
- huey
|
|
155
|
+
- dewey
|
|
156
|
+
- louie
|
|
157
|
+
- fred
|
|
158
|
+
xmas-fifth-day:
|
|
159
|
+
calling-birds: four
|
|
160
|
+
french-hens: 3
|
|
161
|
+
golden-rings: 5
|
|
162
|
+
partridges:
|
|
163
|
+
count: 1
|
|
164
|
+
location: "a pear tree"
|
|
165
|
+
turtle-doves: two`
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const languageMap = {
|
|
169
|
+
json: languages.json,
|
|
170
|
+
js: languages.javascript,
|
|
171
|
+
jsx: languages.javascript,
|
|
172
|
+
javascript: languages.javascript,
|
|
173
|
+
html: languages.html,
|
|
174
|
+
css: languages.css,
|
|
175
|
+
markdown: languages.markdown,
|
|
176
|
+
sh: languages.shell,
|
|
177
|
+
shell: languages.shell,
|
|
178
|
+
bash: languages.shell,
|
|
179
|
+
yml: languages.yml,
|
|
180
|
+
yaml: languages.yml
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const LanguageExamples = () => {
|
|
184
|
+
const [currentLanguage, setCurrentLanguage] = useState('javascript')
|
|
185
|
+
const [currentValue, setCurrentValue] = useState(languageMap.javascript)
|
|
186
|
+
|
|
187
|
+
const languageKeys = Object.keys(languageMap)
|
|
188
|
+
|
|
189
|
+
return (
|
|
190
|
+
<Flex alignItems="start">
|
|
191
|
+
<Flex.Item>
|
|
192
|
+
<RadioInputGroup
|
|
193
|
+
name="languageOptions"
|
|
194
|
+
value={currentLanguage}
|
|
195
|
+
description="Language"
|
|
196
|
+
onChange={(e, newLanguage) => {
|
|
197
|
+
setCurrentLanguage(newLanguage)
|
|
198
|
+
setCurrentValue(languageMap[newLanguage])
|
|
199
|
+
}}
|
|
200
|
+
>
|
|
201
|
+
{languageKeys.map((language) => (
|
|
202
|
+
<RadioInput key={language} label={language} value={language} />
|
|
203
|
+
))}
|
|
204
|
+
</RadioInputGroup>
|
|
205
|
+
</Flex.Item>
|
|
206
|
+
|
|
207
|
+
<Flex.Item padding="0 0 0 large" shouldGrow shouldShrink>
|
|
208
|
+
<FormField label="SourceCodeEditor with syntax highlight">
|
|
209
|
+
<SourceCodeEditor
|
|
210
|
+
label={`${currentLanguage} SourceCodeEditor with syntax highlight`}
|
|
211
|
+
language={currentLanguage}
|
|
212
|
+
value={currentValue}
|
|
213
|
+
onChange={(value) => {
|
|
214
|
+
setCurrentValue(value)
|
|
215
|
+
}}
|
|
216
|
+
lineNumbers
|
|
217
|
+
lineWrapping
|
|
218
|
+
highlightActiveLine
|
|
219
|
+
highlightActiveLineGutter
|
|
220
|
+
/>
|
|
221
|
+
</FormField>
|
|
222
|
+
</Flex.Item>
|
|
223
|
+
</Flex>
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
render(<LanguageExamples />)
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Controlled mode
|
|
231
|
+
|
|
232
|
+
SourceCodeEditor works best as an uncontrolled component (with the `defaultValue` prop), and that is how we recommend it to be used. As an uncontrolled component, the underlying CodeMirror component can take care of all interactions.
|
|
233
|
+
|
|
234
|
+
We've implemented the "controlled" usage, but please let us know if you run into any performance issues or bugs.
|
|
235
|
+
|
|
236
|
+
```js
|
|
237
|
+
---
|
|
238
|
+
type: example
|
|
239
|
+
---
|
|
240
|
+
const ControlledExample = () => {
|
|
241
|
+
const [value, setValue] = useState(`const fruit: string = "apple"
|
|
242
|
+
|
|
243
|
+
function exampleMethod(props: Props) {
|
|
244
|
+
return props ? props.value : null
|
|
245
|
+
}`)
|
|
246
|
+
|
|
247
|
+
const textAreaRef = useRef(null)
|
|
248
|
+
|
|
249
|
+
return (
|
|
250
|
+
<View display="block" background="primary">
|
|
251
|
+
<Flex alignItems="start">
|
|
252
|
+
<Flex.Item shouldGrow shouldShrink padding="0 large 0 0">
|
|
253
|
+
<FormField
|
|
254
|
+
label="Controlled code editor"
|
|
255
|
+
id="controlledCodeEditor"
|
|
256
|
+
messages={[
|
|
257
|
+
{
|
|
258
|
+
type: 'hint',
|
|
259
|
+
text: 'Type in the editor or set the value from the textarea.'
|
|
260
|
+
}
|
|
261
|
+
]}
|
|
262
|
+
>
|
|
263
|
+
<SourceCodeEditor
|
|
264
|
+
label="controlled code editor"
|
|
265
|
+
value={value}
|
|
266
|
+
onChange={(newValue) => setValue(newValue)}
|
|
267
|
+
highlightActiveLine
|
|
268
|
+
highlightActiveLineGutter
|
|
269
|
+
lineWrapping
|
|
270
|
+
lineNumbers
|
|
271
|
+
foldGutter
|
|
272
|
+
spellcheck
|
|
273
|
+
/>
|
|
274
|
+
</FormField>
|
|
275
|
+
</Flex.Item>
|
|
276
|
+
<Flex.Item size="50%" padding="0 0 0 large">
|
|
277
|
+
<FormFieldGroup
|
|
278
|
+
description="Set value from the outside"
|
|
279
|
+
name="setValue"
|
|
280
|
+
>
|
|
281
|
+
<TextArea
|
|
282
|
+
label={<ScreenReaderContent>Change value</ScreenReaderContent>}
|
|
283
|
+
textareaRef={(e) => {
|
|
284
|
+
textAreaRef.current = e
|
|
285
|
+
}}
|
|
286
|
+
defaultValue={value}
|
|
287
|
+
/>
|
|
288
|
+
<Button
|
|
289
|
+
color="primary"
|
|
290
|
+
onClick={() => {
|
|
291
|
+
if (textAreaRef.current) {
|
|
292
|
+
setValue(textAreaRef.current.value)
|
|
293
|
+
}
|
|
294
|
+
}}
|
|
295
|
+
>
|
|
296
|
+
Update value
|
|
297
|
+
</Button>
|
|
298
|
+
</FormFieldGroup>
|
|
299
|
+
</Flex.Item>
|
|
300
|
+
</Flex>
|
|
301
|
+
</View>
|
|
302
|
+
)
|
|
303
|
+
}
|
|
304
|
+
render(<ControlledExample />)
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Editable and readOnly
|
|
308
|
+
|
|
309
|
+
The editability of the content can be set with the combination of the `editable` and `readOnly` props.
|
|
310
|
+
|
|
311
|
+
The `readOnly` prop works like a "preventDefault" and disables any interaction by the user or API calls (e.g. copy-paste).
|
|
312
|
+
If the `editable` prop is set to `false`, the editor is also not focusable, and the `contenteditable="false"` is set on the content.
|
|
313
|
+
|
|
314
|
+
```js
|
|
315
|
+
---
|
|
316
|
+
type: example
|
|
317
|
+
---
|
|
318
|
+
const EditableExample = () => {
|
|
319
|
+
const [settings, setSettings] = useState({
|
|
320
|
+
editable: true,
|
|
321
|
+
readOnly: false
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
return (
|
|
325
|
+
<View display="block" padding="medium medium small" background="primary">
|
|
326
|
+
<View display="block" margin="small none large">
|
|
327
|
+
<FormFieldGroup description="Settings" rowSpacing="small">
|
|
328
|
+
{Object.keys(settings).map((prop) => (
|
|
329
|
+
<Checkbox
|
|
330
|
+
label={prop}
|
|
331
|
+
key={prop}
|
|
332
|
+
defaultChecked={settings[prop]}
|
|
333
|
+
onChange={() =>
|
|
334
|
+
setSettings((prevState) => ({
|
|
335
|
+
...prevState,
|
|
336
|
+
[prop]: !prevState[prop]
|
|
337
|
+
}))
|
|
338
|
+
}
|
|
339
|
+
/>
|
|
340
|
+
))}
|
|
341
|
+
</FormFieldGroup>
|
|
342
|
+
</View>
|
|
343
|
+
|
|
344
|
+
<SourceCodeEditor
|
|
345
|
+
label="editable code editor"
|
|
346
|
+
language="jsx"
|
|
347
|
+
editable={settings.editable}
|
|
348
|
+
readOnly={settings.readOnly}
|
|
349
|
+
defaultValue={`function example() {
|
|
350
|
+
console.log('example')
|
|
351
|
+
}`}
|
|
352
|
+
/>
|
|
353
|
+
</View>
|
|
354
|
+
)
|
|
355
|
+
}
|
|
356
|
+
render(<EditableExample />)
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Gutter settings
|
|
360
|
+
|
|
361
|
+
The `lineNumbers` prop displays the line numbers in the side gutter, and the `foldGutter` prop displays the toggleable fold icon next to foldable code blocks.
|
|
362
|
+
|
|
363
|
+
If any of these two props are active, the gutter is displayed, and the `highlightActiveLineGutter` highlights the active line in the gutter. (The `highlightActiveLine` prop highlights the line itself.)
|
|
364
|
+
|
|
365
|
+
```js
|
|
366
|
+
---
|
|
367
|
+
type: example
|
|
368
|
+
---
|
|
369
|
+
const GutterExample = () => {
|
|
370
|
+
const [settings, setSettings] = useState({
|
|
371
|
+
lineNumbers: true,
|
|
372
|
+
foldGutter: true,
|
|
373
|
+
highlightActiveLineGutter: true,
|
|
374
|
+
highlightActiveLine: true
|
|
375
|
+
})
|
|
376
|
+
|
|
377
|
+
return (
|
|
378
|
+
<View display="block" padding="medium medium small" background="primary">
|
|
379
|
+
<View display="block" margin="small none large">
|
|
380
|
+
<FormFieldGroup description="Settings" rowSpacing="small">
|
|
381
|
+
{[
|
|
382
|
+
'lineNumbers',
|
|
383
|
+
'foldGutter',
|
|
384
|
+
'highlightActiveLineGutter',
|
|
385
|
+
'highlightActiveLine'
|
|
386
|
+
].map((prop) => (
|
|
387
|
+
<Checkbox
|
|
388
|
+
label={prop}
|
|
389
|
+
key={prop}
|
|
390
|
+
defaultChecked={settings[prop]}
|
|
391
|
+
onChange={() => {
|
|
392
|
+
setSettings((prevSettings) => ({
|
|
393
|
+
...prevSettings,
|
|
394
|
+
[prop]: !prevSettings[prop]
|
|
395
|
+
}))
|
|
396
|
+
}}
|
|
397
|
+
/>
|
|
398
|
+
))}
|
|
399
|
+
</FormFieldGroup>
|
|
400
|
+
</View>
|
|
401
|
+
|
|
402
|
+
<SourceCodeEditor
|
|
403
|
+
label="gutter example"
|
|
404
|
+
language="jsx"
|
|
405
|
+
lineNumbers={settings.lineNumbers}
|
|
406
|
+
foldGutter={settings.foldGutter}
|
|
407
|
+
highlightActiveLineGutter={settings.highlightActiveLineGutter}
|
|
408
|
+
highlightActiveLine={settings.highlightActiveLine}
|
|
409
|
+
defaultValue={`const fruit: string = "apple"
|
|
410
|
+
|
|
411
|
+
function exampleMethod(props: Props) {
|
|
412
|
+
return props ? props.value : null
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* This is an example
|
|
417
|
+
* @param {Object} props
|
|
418
|
+
*/
|
|
419
|
+
const Example = () => {
|
|
420
|
+
return (
|
|
421
|
+
<View as="div" padding={'large'}>
|
|
422
|
+
<Position
|
|
423
|
+
renderTarget={<GoodComponent />}
|
|
424
|
+
placement='end center'
|
|
425
|
+
offsetX='20px'
|
|
426
|
+
>
|
|
427
|
+
<span style={{ padding: '8px', background: 'white' }}>
|
|
428
|
+
Positioned content
|
|
429
|
+
</span>
|
|
430
|
+
</Position>
|
|
431
|
+
</View>
|
|
432
|
+
)
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
render(<Example />)`}
|
|
436
|
+
/>
|
|
437
|
+
</View>
|
|
438
|
+
)
|
|
439
|
+
}
|
|
440
|
+
render(<GutterExample />)
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
### Indentation
|
|
444
|
+
|
|
445
|
+
##### auto-indent
|
|
446
|
+
|
|
447
|
+
The editor automatically indents the lines on input. The `indentOnLoad` prop indents the code on the initial load and when the `value` prop is updated.
|
|
448
|
+
|
|
449
|
+
##### indent with tab
|
|
450
|
+
|
|
451
|
+
When the `indentWithTab` feature is turned on, Tab and Shift-Tab will indent the code.
|
|
452
|
+
By default, it is turned off, and tabbing will focus the next element in the tab order.
|
|
453
|
+
|
|
454
|
+
**Accessibility note**: Even if `indentWithTab` is on, pressing Escape before tabbing will not handle indentation and will handle focus instead. When using this feature, it is recommended to add info about this behaviour in your documentation.
|
|
455
|
+
|
|
456
|
+
##### indent unit
|
|
457
|
+
|
|
458
|
+
You can also override the unit by which indentation happens (defaults to 2 spaces).
|
|
459
|
+
The `indentUnitCount` prop should be a string consisting either entirely of spaces or entirely of tabs.
|
|
460
|
+
|
|
461
|
+
##### manual re-indent
|
|
462
|
+
|
|
463
|
+
Another useful feature is the `indentAll` public method on the `SourceCodeEditor` component that can be called anytime to trigger a re-indent on the content.
|
|
464
|
+
|
|
465
|
+
```js
|
|
466
|
+
---
|
|
467
|
+
type: example
|
|
468
|
+
---
|
|
469
|
+
const IndentExample = () => {
|
|
470
|
+
const [indentWithTab, setIndentWithTab] = useState(true)
|
|
471
|
+
const [indentUnitCount, setIndentUnitCount] = useState('2')
|
|
472
|
+
|
|
473
|
+
const editorRef = useRef(null)
|
|
474
|
+
|
|
475
|
+
const indentUnit = Array(parseInt(indentUnitCount)).fill(' ').join('')
|
|
476
|
+
|
|
477
|
+
const reIndent = () => {
|
|
478
|
+
if (editorRef.current) {
|
|
479
|
+
editorRef.current.indentAll()
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const indentCurrentSelection = () => {
|
|
484
|
+
if (editorRef.current) {
|
|
485
|
+
editorRef.current.indentCurrentSelection()
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
return (
|
|
490
|
+
<View display="block" padding="medium medium small" background="primary">
|
|
491
|
+
<View display="block" margin="small none large">
|
|
492
|
+
<FormFieldGroup description="Settings">
|
|
493
|
+
<Checkbox
|
|
494
|
+
label="indentWithTab"
|
|
495
|
+
defaultChecked={indentWithTab}
|
|
496
|
+
onChange={() => setIndentWithTab(!indentWithTab)}
|
|
497
|
+
/>
|
|
498
|
+
<RadioInputGroup
|
|
499
|
+
name="indentUnitCount"
|
|
500
|
+
value={indentUnitCount}
|
|
501
|
+
description="indent space count"
|
|
502
|
+
onChange={(e, value) => {
|
|
503
|
+
setIndentUnitCount(value)
|
|
504
|
+
reIndent()
|
|
505
|
+
}}
|
|
506
|
+
>
|
|
507
|
+
{['2', '4', '8'].map((count) => (
|
|
508
|
+
<RadioInput key={count} label={count} value={count} />
|
|
509
|
+
))}
|
|
510
|
+
</RadioInputGroup>
|
|
511
|
+
<Button onClick={reIndent}>Re-indent code</Button>
|
|
512
|
+
<Button onClick={indentCurrentSelection}>
|
|
513
|
+
Indent current selection
|
|
514
|
+
</Button>
|
|
515
|
+
</FormFieldGroup>
|
|
516
|
+
</View>
|
|
517
|
+
|
|
518
|
+
<SourceCodeEditor
|
|
519
|
+
label="indent example"
|
|
520
|
+
ref={editorRef}
|
|
521
|
+
language="jsx"
|
|
522
|
+
indentWithTab={indentWithTab}
|
|
523
|
+
indentUnit={indentUnit}
|
|
524
|
+
defaultValue={`const fruit: string = "apple"
|
|
525
|
+
|
|
526
|
+
function exampleMethod(props: Props) {
|
|
527
|
+
return props ? props.value : null
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* This is an example
|
|
532
|
+
* @param {Object} props
|
|
533
|
+
*/
|
|
534
|
+
const Example = () => {
|
|
535
|
+
return (
|
|
536
|
+
<View as="div" padding={'large'}>
|
|
537
|
+
<Position
|
|
538
|
+
renderTarget={<GoodComponent />}
|
|
539
|
+
placement='end center'
|
|
540
|
+
offsetX='20px'
|
|
541
|
+
>
|
|
542
|
+
<span style={{ padding: '8px', background: 'white' }}>
|
|
543
|
+
Positioned content
|
|
544
|
+
</span>
|
|
545
|
+
</Position>
|
|
546
|
+
</View>
|
|
547
|
+
)
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
render(<Example />)`}
|
|
551
|
+
/>
|
|
552
|
+
</View>
|
|
553
|
+
)
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
render(<IndentExample />)
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
### Direction
|
|
560
|
+
|
|
561
|
+
SourceCodeEditor is a bidirectional component. It will inherit the text-direction from the context, and can be set directly on the component with the `direction` prop. The `rtl` mode will flip the overall layout and selects base paragraph direction to RTL.
|
|
562
|
+
|
|
563
|
+
The `rtlMoveVisually` prop controls the cursor movement in RTL mode, whether it should be **visual** (pressing the left arrow moves the cursor left) or **logical** (pressing the left arrow moves to the next lower index in the string, which is visually right in RTL text).
|
|
564
|
+
|
|
565
|
+
```js
|
|
566
|
+
---
|
|
567
|
+
type: example
|
|
568
|
+
---
|
|
569
|
+
const DirectionExample = () => {
|
|
570
|
+
const [settings, setSettings] = useState({
|
|
571
|
+
contextDir: 'unset',
|
|
572
|
+
editorDir: 'unset',
|
|
573
|
+
rtlMoveVisually: true
|
|
574
|
+
})
|
|
575
|
+
|
|
576
|
+
return (
|
|
577
|
+
<InstUISettingsProvider
|
|
578
|
+
dir={settings.contextDir !== 'unset' ? settings.contextDir : undefined}
|
|
579
|
+
>
|
|
580
|
+
<View
|
|
581
|
+
display="block"
|
|
582
|
+
padding="medium medium small"
|
|
583
|
+
background="primary"
|
|
584
|
+
>
|
|
585
|
+
<View display="block" margin="small none large">
|
|
586
|
+
<FormFieldGroup
|
|
587
|
+
description="Settings"
|
|
588
|
+
layout="columns"
|
|
589
|
+
vAlign="top"
|
|
590
|
+
>
|
|
591
|
+
<RadioInputGroup
|
|
592
|
+
name="contextDir"
|
|
593
|
+
value={settings.contextDir}
|
|
594
|
+
description="context direction"
|
|
595
|
+
onChange={(e, contextDir) => {
|
|
596
|
+
setSettings((prevSettings) => ({
|
|
597
|
+
...prevSettings,
|
|
598
|
+
contextDir
|
|
599
|
+
}))
|
|
600
|
+
}}
|
|
601
|
+
>
|
|
602
|
+
{['unset', 'ltr', 'rtl'].map((dir) => (
|
|
603
|
+
<RadioInput key={dir} label={dir} value={dir} />
|
|
604
|
+
))}
|
|
605
|
+
</RadioInputGroup>
|
|
606
|
+
<RadioInputGroup
|
|
607
|
+
name="editorDir"
|
|
608
|
+
value={settings.editorDir}
|
|
609
|
+
description="editor direction"
|
|
610
|
+
onChange={(e, editorDir) => {
|
|
611
|
+
setSettings((prevSettings) => ({
|
|
612
|
+
...prevSettings,
|
|
613
|
+
editorDir
|
|
614
|
+
}))
|
|
615
|
+
}}
|
|
616
|
+
>
|
|
617
|
+
{['unset', 'ltr', 'rtl'].map((dir) => (
|
|
618
|
+
<RadioInput key={dir} label={dir} value={dir} />
|
|
619
|
+
))}
|
|
620
|
+
</RadioInputGroup>
|
|
621
|
+
<Checkbox
|
|
622
|
+
label="rtlMoveVisually"
|
|
623
|
+
defaultChecked={settings.rtlMoveVisually}
|
|
624
|
+
onChange={() => {
|
|
625
|
+
setSettings((prevSettings) => ({
|
|
626
|
+
...prevSettings,
|
|
627
|
+
rtlMoveVisually: !prevSettings.rtlMoveVisually
|
|
628
|
+
}))
|
|
629
|
+
}}
|
|
630
|
+
/>
|
|
631
|
+
</FormFieldGroup>
|
|
632
|
+
</View>
|
|
633
|
+
|
|
634
|
+
<SourceCodeEditor
|
|
635
|
+
label="editable code editor"
|
|
636
|
+
language="jsx"
|
|
637
|
+
direction={
|
|
638
|
+
settings.editorDir !== 'unset' ? settings.editorDir : undefined
|
|
639
|
+
}
|
|
640
|
+
rtlMoveVisually={settings.rtlMoveVisually}
|
|
641
|
+
defaultValue={`function directionExample(dir?: 'ltr' | 'rtl') {
|
|
642
|
+
console.log(dir)
|
|
643
|
+
}`}
|
|
644
|
+
/>
|
|
645
|
+
</View>
|
|
646
|
+
</InstUISettingsProvider>
|
|
647
|
+
)
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
render(<DirectionExample />)
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
### Focus management
|
|
654
|
+
|
|
655
|
+
By default, SourceCodeEditor is tabbable/focusable, and once it is in focus, tabbing will move the focus on the page. These behaviours can be changed with the [editable](/#SourceCodeEditor/#editable-and-readonly) and [indentWithTab](/#SourceCodeEditor/#indentation-indent-with-tab) props.
|
|
656
|
+
|
|
657
|
+
The `autofocus` prop will automatically focus the editor on the initial render.
|
|
658
|
+
|
|
659
|
+
You can also manually focus the editor with its public `focus` method (the `hasFocus` getter is also available).
|
|
660
|
+
|
|
661
|
+
```js
|
|
662
|
+
---
|
|
663
|
+
type: example
|
|
664
|
+
---
|
|
665
|
+
const FocusExample = () => {
|
|
666
|
+
const [indentWithTab, setIndentWithTab] = useState(true)
|
|
667
|
+
const [indentUnitCount, setIndentUnitCount] = useState('2')
|
|
668
|
+
|
|
669
|
+
const editorRef = useRef(null)
|
|
670
|
+
|
|
671
|
+
return (
|
|
672
|
+
<View display="block" padding="medium medium small" background="primary">
|
|
673
|
+
<View display="block" margin="small none large">
|
|
674
|
+
<Button
|
|
675
|
+
onClick={() => {
|
|
676
|
+
console.log('manual focus')
|
|
677
|
+
editorRef.current.focus()
|
|
678
|
+
}}
|
|
679
|
+
>
|
|
680
|
+
Focus editor
|
|
681
|
+
</Button>
|
|
682
|
+
</View>
|
|
683
|
+
|
|
684
|
+
<SourceCodeEditor
|
|
685
|
+
label="focus example"
|
|
686
|
+
ref={editorRef}
|
|
687
|
+
language="jsx"
|
|
688
|
+
onFocus={() => {
|
|
689
|
+
console.log('onFocus')
|
|
690
|
+
console.log({ hasFocus: editorRef.current.hasFocus })
|
|
691
|
+
}}
|
|
692
|
+
onBlur={() => {
|
|
693
|
+
console.log('onBlur')
|
|
694
|
+
console.log({ hasFocus: editorRef.current.hasFocus })
|
|
695
|
+
}}
|
|
696
|
+
defaultValue={`function exampleMethod(props: Props) {
|
|
697
|
+
return props ? props.value : null
|
|
698
|
+
}`}
|
|
699
|
+
/>
|
|
700
|
+
</View>
|
|
701
|
+
)
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
render(<FocusExample />)
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
### Attachment
|
|
708
|
+
|
|
709
|
+
The `attachment` prop removes the top/bottom border-radius and margin of the editor, so it can be attached to the top or bottom of another element.
|
|
710
|
+
|
|
711
|
+
```js
|
|
712
|
+
---
|
|
713
|
+
type: example
|
|
714
|
+
---
|
|
715
|
+
const AttachmentExample = () => {
|
|
716
|
+
const [attachment, setAttachment] = useState('none')
|
|
717
|
+
|
|
718
|
+
const viewProps = {
|
|
719
|
+
as: 'div',
|
|
720
|
+
background: 'primary-inverse',
|
|
721
|
+
padding: 'small'
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
return (
|
|
725
|
+
<View display="block" padding="medium medium small" background="primary">
|
|
726
|
+
<View display="block" margin="small none large">
|
|
727
|
+
<RadioInputGroup
|
|
728
|
+
name="attachmentExample"
|
|
729
|
+
value={attachment}
|
|
730
|
+
description="attachment"
|
|
731
|
+
onChange={(e, attachment) => {
|
|
732
|
+
setAttachment(attachment)
|
|
733
|
+
}}
|
|
734
|
+
>
|
|
735
|
+
{['none', 'top', 'bottom'].map((attachment) => (
|
|
736
|
+
<RadioInput
|
|
737
|
+
key={attachment}
|
|
738
|
+
label={attachment}
|
|
739
|
+
value={attachment}
|
|
740
|
+
/>
|
|
741
|
+
))}
|
|
742
|
+
</RadioInputGroup>
|
|
743
|
+
</View>
|
|
744
|
+
|
|
745
|
+
{attachment === 'bottom' && (
|
|
746
|
+
<View {...viewProps}>
|
|
747
|
+
CodeEditor is attached to the bottom of this element
|
|
748
|
+
</View>
|
|
749
|
+
)}
|
|
750
|
+
<SourceCodeEditor
|
|
751
|
+
label="attachment example"
|
|
752
|
+
language="jsx"
|
|
753
|
+
attachment={attachment === 'none' ? undefined : attachment}
|
|
754
|
+
defaultValue={`const fruit: string = "apple"
|
|
755
|
+
|
|
756
|
+
function exampleMethod(props: Props) {
|
|
757
|
+
return props ? props.value : null
|
|
758
|
+
}`}
|
|
759
|
+
/>
|
|
760
|
+
{attachment === 'top' && (
|
|
761
|
+
<View {...viewProps}>
|
|
762
|
+
CodeEditor is attached to the top of this element
|
|
763
|
+
</View>
|
|
764
|
+
)}
|
|
765
|
+
</View>
|
|
766
|
+
)
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
render(<AttachmentExample />)
|
|
770
|
+
```
|
|
771
|
+
|
|
772
|
+
### Search
|
|
773
|
+
|
|
774
|
+
To enable the search panel use the `searchConfig` prop.
|
|
775
|
+
|
|
776
|
+
You can open the search panel in the code editor by pressing `cmd/ctrl+f` when the editor is in focus (otherwise the browser's search will open). The reason you would use this instead of the browser native search is because it will miss results that are far out of view in the text rendered by the editor. This is the limitation of the underlying CodeMirror component.
|
|
777
|
+
|
|
778
|
+
Hitting `Enter` jumps to the next result and `Shift+Enter` to the previous. Alternatively you can use the up and down buttons to the right of the input field.
|
|
779
|
+
|
|
780
|
+
Placement: the search panel will open at the bottom of the editor. This cannot be changed unfortunately due to a limitation in CodeMirror.
|
|
781
|
+
|
|
782
|
+
```js
|
|
783
|
+
---
|
|
784
|
+
type: example
|
|
785
|
+
---
|
|
786
|
+
<SourceCodeEditor
|
|
787
|
+
label="lorem"
|
|
788
|
+
language="markdown"
|
|
789
|
+
defaultValue={`Lorem ipsum dolor sit amet, officia excepteur ex fugiat reprehenderit
|
|
790
|
+
enim labore culpa sint ad nisi Lorem pariatur mollit ex esse exercitation amet.
|
|
791
|
+
Nisi anim cupidatat excepteur officia.
|
|
792
|
+
`}
|
|
793
|
+
searchConfig={{
|
|
794
|
+
placeholder: 'Find in code...',
|
|
795
|
+
nextResultLabel: 'Next result',
|
|
796
|
+
prevResultLabel: 'Previouse result',
|
|
797
|
+
}}
|
|
798
|
+
/>
|
|
799
|
+
```
|