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