@instructure/ui-source-code-editor 10.12.0 → 10.12.1-pr-snapshot-1741273500459

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.
@@ -54,190 +54,370 @@ Setting the correct language adds **syntax highlighting** and other helpful feat
54
54
 
55
55
  **Note:** In case you need support for additional languages, please contact us on [GitHub](https://github.com/instructure/instructure-ui)!
56
56
 
57
- ```js
58
- ---
59
- type: example
60
- ---
57
+ - ```js
58
+ const languages = {
59
+ json: `{
60
+ "name": "@instructure/ui-source-code-editor",
61
+ "version": "8.24.2",
62
+ "description": "A UI component library made by Instructure Inc.",
63
+ "author": "Instructure, Inc. Engineering and Product Design",
64
+ "module": "./es/index.js",
65
+ "main": "./lib/index.js",
66
+ "types": "./types/index.d.ts",
67
+ "repository": {
68
+ "type": "git",
69
+ "url": "https://github.com/instructure/instructure-ui.git"
70
+ },
71
+ }`,
72
+ javascript: `const fruit: string = "apple"
73
+
74
+ const re = new RegExp('ab+c')
75
+
76
+ function exampleMethod(props: Props) {
77
+ return props ? props.value : null
78
+ }
79
+
80
+ /**
81
+ * This is an example
82
+ * @param {Object} props
83
+ */
84
+ const Example = () => {
85
+ return (
86
+ <View as="div" padding={'large'}>
87
+ <Position
88
+ renderTarget={<GoodComponent />}
89
+ placement='end center'
90
+ offsetX='20px'
91
+ >
92
+ <span style={{ padding: '8px', background: 'white' }}>
93
+ Positioned content
94
+ </span>
95
+ </Position>
96
+ </View>
97
+ )
98
+ }
99
+
100
+ render(<Example />)`,
101
+ html: `<!DOCTYPE html>
102
+ <html lang="en">
103
+ <head>
104
+ <meta charset="UTF-8" />
105
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
106
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
107
+ <title>Example app</title>
108
+ </head>
109
+ <body>
110
+ <div id="app">
111
+ <button onclick="myFunction()">Click me</button>
112
+ </div>
113
+
114
+ <script src="script.js"></script>
115
+ </body>
116
+ </html>`,
117
+ css: `a {
118
+ text-decoration: none;
119
+
120
+ &:hover { text-decoration: underline; }
121
+ }
122
+
123
+ a:link, a:visited, a:hover, a:active {
124
+ background-color: green;
125
+ color: white;
126
+ padding: 10px 25px;
127
+ text-align: center;
128
+ text-decoration: none;
129
+ display: inline-block;
130
+ }
131
+
132
+ .centertext { text-align: center; }
133
+
134
+ img { opacity: 0.5; filter: alpha(opacity=50); }`,
135
+ markdown: `#### The quarterly results look great!
136
+
137
+ > - Revenue was off the chart.
138
+ > - Profits were higher than ever.
139
+
140
+ *Everything* is going according to **plan**.
141
+
142
+ ---
143
+ type: example
144
+ ---`,
145
+ shell: `#!/bin/bash
146
+
147
+ # example of using arguments to a script
148
+ echo "My first name is $1"
149
+ echo "My surname is $2"
150
+ echo "Total number of arguments is $#"
151
+
152
+ ________________________________________
153
+
154
+ $ chmod a+x name.sh
155
+ $ ./name.sh Hans-Wolfgang Loidl
156
+ My first name is Hans-Wolfgang
157
+ My surname is Loidl
158
+ Total number of arguments is 2`,
159
+ yml: `---
160
+ doe: "a deer, a female deer"
161
+ ray: "a drop of golden sun"
162
+ pi: 3.14159
163
+ xmas: true
164
+ french-hens: 3
165
+ calling-birds:
166
+ - huey
167
+ - dewey
168
+ - louie
169
+ - fred
170
+ xmas-fifth-day:
171
+ calling-birds: four
172
+ french-hens: 3
173
+ golden-rings: 5
174
+ partridges:
175
+ count: 1
176
+ location: "a pear tree"
177
+ turtle-doves: two`
178
+ }
61
179
 
62
- const languages = {
63
- json: `{
64
- "name": "@instructure/ui-source-code-editor",
65
- "version": "8.24.2",
66
- "description": "A UI component library made by Instructure Inc.",
67
- "author": "Instructure, Inc. Engineering and Product Design",
68
- "module": "./es/index.js",
69
- "main": "./lib/index.js",
70
- "types": "./types/index.d.ts",
71
- "repository": {
72
- "type": "git",
73
- "url": "https://github.com/instructure/instructure-ui.git"
74
- },
75
- }`,
76
- javascript: `const fruit: string = "apple"
77
-
78
- const re = new RegExp('ab+c')
79
-
80
- function exampleMethod(props: Props) {
81
- return props ? props.value : null
82
- }
180
+ const languageMap = {
181
+ json: languages.json,
182
+ js: languages.javascript,
183
+ jsx: languages.javascript,
184
+ javascript: languages.javascript,
185
+ html: languages.html,
186
+ css: languages.css,
187
+ markdown: languages.markdown,
188
+ sh: languages.shell,
189
+ shell: languages.shell,
190
+ bash: languages.shell,
191
+ yml: languages.yml,
192
+ yaml: languages.yml
193
+ }
83
194
 
84
- /**
85
- * This is an example
86
- * @param {Object} props
87
- */
88
- const Example = () => {
89
- return (
90
- <View as="div" padding={'large'}>
91
- <Position
92
- renderTarget={<GoodComponent />}
93
- placement='end center'
94
- offsetX='20px'
95
- >
96
- <span style={{ padding: '8px', background: 'white' }}>
97
- Positioned content
98
- </span>
99
- </Position>
100
- </View>
101
- )
102
- }
103
-
104
- render(<Example />)`,
105
- html: `<!DOCTYPE html>
106
- <html lang="en">
107
- <head>
108
- <meta charset="UTF-8" />
109
- <meta http-equiv="X-UA-Compatible" content="IE=edge" />
110
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
111
- <title>Example app</title>
112
- </head>
113
- <body>
114
- <div id="app">
115
- <button onclick="myFunction()">Click me</button>
116
- </div>
117
-
118
- <script src="script.js"></script>
119
- </body>
120
- </html>`,
121
- css: `a {
122
- text-decoration: none;
123
-
124
- &:hover { text-decoration: underline; }
125
- }
126
-
127
- a:link, a:visited, a:hover, a:active {
128
- background-color: green;
129
- color: white;
130
- padding: 10px 25px;
131
- text-align: center;
132
- text-decoration: none;
133
- display: inline-block;
134
- }
135
-
136
- .centertext { text-align: center; }
137
-
138
- img { opacity: 0.5; filter: alpha(opacity=50); }`,
139
- markdown: `#### The quarterly results look great!
140
-
141
- > - Revenue was off the chart.
142
- > - Profits were higher than ever.
143
-
144
- *Everything* is going according to **plan**.
195
+ class LanguageExamples extends React.Component {
196
+ state = {
197
+ currentLanguage: 'javascript',
198
+ currentValue: languageMap.javascript
199
+ }
200
+
201
+ render() {
202
+ const languageKeys = Object.keys(languageMap)
203
+
204
+ return (
205
+ <Flex alignItems="start">
206
+ <Flex.Item>
207
+ <RadioInputGroup
208
+ name="languageOptions"
209
+ value={this.state.currentLanguage}
210
+ description="Language"
211
+ onChange={(e, currentLanguage) => {
212
+ this.setState({
213
+ currentLanguage,
214
+ currentValue: languageMap[currentLanguage]
215
+ })
216
+ }}
217
+ >
218
+ {languageKeys.map((language) => (
219
+ <RadioInput key={language} label={language} value={language} />
220
+ ))}
221
+ </RadioInputGroup>
222
+ </Flex.Item>
223
+
224
+ <Flex.Item padding="0 0 0 large" shouldGrow shouldShrink>
225
+ <FormField label="SourceCodeEditor with syntax highlight">
226
+ <SourceCodeEditor
227
+ label={`${this.state.currentLanguage} code editor`}
228
+ language={this.state.currentLanguage}
229
+ value={this.state.currentValue}
230
+ onChange={(value) => {
231
+ this.setState({
232
+ currentValue: value
233
+ })
234
+ }}
235
+ lineNumbers
236
+ lineWrapping
237
+ highlightActiveLine
238
+ highlightActiveLineGutter
239
+ />
240
+ </FormField>
241
+ </Flex.Item>
242
+ </Flex>
243
+ )
244
+ }
245
+ }
246
+
247
+ render(<LanguageExamples />)
248
+ ```
249
+
250
+ - ```js
251
+ const languages = {
252
+ json: `{
253
+ "name": "@instructure/ui-source-code-editor",
254
+ "version": "8.24.2",
255
+ "description": "A UI component library made by Instructure Inc.",
256
+ "author": "Instructure, Inc. Engineering and Product Design",
257
+ "module": "./es/index.js",
258
+ "main": "./lib/index.js",
259
+ "types": "./types/index.d.ts",
260
+ "repository": {
261
+ "type": "git",
262
+ "url": "https://github.com/instructure/instructure-ui.git"
263
+ },
264
+ }`,
265
+ javascript: `const fruit: string = "apple"
266
+
267
+ const re = new RegExp('ab+c')
268
+
269
+ function exampleMethod(props: Props) {
270
+ return props ? props.value : null
271
+ }
272
+
273
+ /**
274
+ * This is an example
275
+ * @param {Object} props
276
+ */
277
+ const Example = () => {
278
+ return (
279
+ <View as="div" padding={'large'}>
280
+ <Position
281
+ renderTarget={<GoodComponent />}
282
+ placement='end center'
283
+ offsetX='20px'
284
+ >
285
+ <span style={{ padding: '8px', background: 'white' }}>
286
+ Positioned content
287
+ </span>
288
+ </Position>
289
+ </View>
290
+ )
291
+ }
292
+
293
+ render(<Example />)`,
294
+
295
+ html: `<!DOCTYPE html>
296
+ <html lang="en">
297
+ <head>
298
+ <meta charset="UTF-8" />
299
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
300
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
301
+ <title>Example app</title>
302
+ </head>
303
+ <body>
304
+ <div id="app">
305
+ <button onclick="myFunction()">Click me</button>
306
+ </div>
307
+
308
+ <script src="script.js"></script>
309
+ </body>
310
+ </html>`,
311
+ css: `a {
312
+ text-decoration: none;
313
+
314
+ &:hover { text-decoration: underline; }
315
+ }
316
+
317
+ a:link, a:visited, a:hover, a:active {
318
+ background-color: green;
319
+ color: white;
320
+ padding: 10px 25px;
321
+ text-align: center;
322
+ text-decoration: none;
323
+ display: inline-block;
324
+ }
325
+
326
+ .centertext { text-align: center; }
327
+
328
+ img { opacity: 0.5; filter: alpha(opacity=50); }`,
329
+ markdown: `#### The quarterly results look great!
330
+
331
+ > - Revenue was off the chart.
332
+ > - Profits were higher than ever.
333
+
334
+ *Everything* is going according to **plan**.
335
+
336
+ ---
337
+ type: example
338
+ ---`,
339
+ shell: `#!/bin/bash
340
+
341
+ # example of using arguments to a script
342
+ echo "My first name is $1"
343
+ echo "My surname is $2"
344
+ echo "Total number of arguments is $#"
345
+
346
+ ________________________________________
347
+
348
+ $ chmod a+x name.sh
349
+ $ ./name.sh Hans-Wolfgang Loidl
350
+ My first name is Hans-Wolfgang
351
+ My surname is Loidl
352
+ Total number of arguments is 2`,
353
+ yml: `---
354
+ doe: "a deer, a female deer"
355
+ ray: "a drop of golden sun"
356
+ pi: 3.14159
357
+ xmas: true
358
+ french-hens: 3
359
+ calling-birds:
360
+ - huey
361
+ - dewey
362
+ - louie
363
+ - fred
364
+ xmas-fifth-day:
365
+ calling-birds: four
366
+ french-hens: 3
367
+ golden-rings: 5
368
+ partridges:
369
+ count: 1
370
+ location: "a pear tree"
371
+ turtle-doves: two`
372
+ }
373
+
374
+ const languageMap = {
375
+ json: languages.json,
376
+ js: languages.javascript,
377
+ jsx: languages.javascript,
378
+ javascript: languages.javascript,
379
+ html: languages.html,
380
+ css: languages.css,
381
+ markdown: languages.markdown,
382
+ sh: languages.shell,
383
+ shell: languages.shell,
384
+ bash: languages.shell,
385
+ yml: languages.yml,
386
+ yaml: languages.yml
387
+ }
388
+
389
+ const LanguageExamples = () => {
390
+ const [currentLanguage, setCurrentLanguage] = useState('javascript')
391
+ const [currentValue, setCurrentValue] = useState(languageMap.javascript)
145
392
 
146
- ---
147
- type: example
148
- ---`,
149
- shell: `#!/bin/bash
150
-
151
- # example of using arguments to a script
152
- echo "My first name is $1"
153
- echo "My surname is $2"
154
- echo "Total number of arguments is $#"
155
-
156
- ________________________________________
157
-
158
- $ chmod a+x name.sh
159
- $ ./name.sh Hans-Wolfgang Loidl
160
- My first name is Hans-Wolfgang
161
- My surname is Loidl
162
- Total number of arguments is 2`,
163
- yml: `---
164
- doe: "a deer, a female deer"
165
- ray: "a drop of golden sun"
166
- pi: 3.14159
167
- xmas: true
168
- french-hens: 3
169
- calling-birds:
170
- - huey
171
- - dewey
172
- - louie
173
- - fred
174
- xmas-fifth-day:
175
- calling-birds: four
176
- french-hens: 3
177
- golden-rings: 5
178
- partridges:
179
- count: 1
180
- location: "a pear tree"
181
- turtle-doves: two`
182
- }
183
-
184
- const languageMap = {
185
- json: languages.json,
186
- js: languages.javascript,
187
- jsx: languages.javascript,
188
- javascript: languages.javascript,
189
- html: languages.html,
190
- css: languages.css,
191
- markdown: languages.markdown,
192
- sh: languages.shell,
193
- shell: languages.shell,
194
- bash: languages.shell,
195
- yml: languages.yml,
196
- yaml: languages.yml,
197
- }
198
-
199
- class LanguageExamples extends React.Component {
200
- state = {
201
- currentLanguage: 'javascript',
202
- currentValue: languageMap.javascript,
203
- }
204
-
205
- render() {
206
393
  const languageKeys = Object.keys(languageMap)
207
394
 
208
395
  return (
209
- <Flex alignItems='start'>
396
+ <Flex alignItems="start">
210
397
  <Flex.Item>
211
398
  <RadioInputGroup
212
399
  name="languageOptions"
213
- value={this.state.currentLanguage}
400
+ value={currentLanguage}
214
401
  description="Language"
215
- onChange={(e, currentLanguage) => {
216
- this.setState({
217
- currentLanguage,
218
- currentValue: languageMap[currentLanguage]
219
- })
402
+ onChange={(e, newLanguage) => {
403
+ setCurrentLanguage(newLanguage)
404
+ setCurrentValue(languageMap[newLanguage])
220
405
  }}
221
406
  >
222
- {languageKeys.map(language => (
223
- <RadioInput
224
- key={language}
225
- label={language}
226
- value={language}
227
- />))}
407
+ {languageKeys.map((language) => (
408
+ <RadioInput key={language} label={language} value={language} />
409
+ ))}
228
410
  </RadioInputGroup>
229
411
  </Flex.Item>
230
412
 
231
413
  <Flex.Item padding="0 0 0 large" shouldGrow shouldShrink>
232
414
  <FormField label="SourceCodeEditor with syntax highlight">
233
415
  <SourceCodeEditor
234
- label={`${this.state.currentLanguage} SourceCodeEditor with syntax highlight`}
235
- language={this.state.currentLanguage}
236
- value={this.state.currentValue}
416
+ label={`${currentLanguage} SourceCodeEditor with syntax highlight`}
417
+ language={currentLanguage}
418
+ value={currentValue}
237
419
  onChange={(value) => {
238
- this.setState({
239
- currentValue: value
240
- })
420
+ setCurrentValue(value)
241
421
  }}
242
422
  lineNumbers
243
423
  lineWrapping
@@ -249,11 +429,9 @@ class LanguageExamples extends React.Component {
249
429
  </Flex>
250
430
  )
251
431
  }
252
- }
253
432
 
254
- render(<LanguageExamples />)
255
-
256
- ```
433
+ render(<LanguageExamples />)
434
+ ```
257
435
 
258
436
  ### Controlled mode
259
437
 
@@ -261,40 +439,109 @@ SourceCodeEditor works best as an uncontrolled component (with the `defaultValue
261
439
 
262
440
  We've implemented the "controlled" usage, but please let us know if you run into any performance issues or bugs.
263
441
 
264
- ```js
265
- ---
266
- type: example
267
- ---
268
- class ControlledExample extends React.Component {
269
- state = {
270
- value: `const fruit: string = "apple"
442
+ - ```js
443
+ class ControlledExample extends React.Component {
444
+ state = {
445
+ value: `const fruit: string = "apple"
446
+
447
+ function exampleMethod(props: Props) {
448
+ return props ? props.value : null
449
+ }`
450
+ }
271
451
 
272
- function exampleMethod(props: Props) {
273
- return props ? props.value : null
274
- }`
452
+ textAreaRef = null
453
+
454
+ render() {
455
+ return (
456
+ <View display="block" background="primary">
457
+ <Flex alignItems="start">
458
+ <Flex.Item shouldGrow shouldShrink padding="0 large 0 0">
459
+ <FormField
460
+ label="Controlled code editor"
461
+ id="controlledCodeEditor"
462
+ messages={[
463
+ {
464
+ type: 'hint',
465
+ text: 'Type in the editor or set the value from the textarea.'
466
+ }
467
+ ]}
468
+ >
469
+ <SourceCodeEditor
470
+ label="controlled code editor"
471
+ value={this.state.value}
472
+ onChange={(value) => {
473
+ this.setState({ value })
474
+ }}
475
+ highlightActiveLine
476
+ highlightActiveLineGutter
477
+ lineWrapping
478
+ lineNumbers
479
+ foldGutter
480
+ spellcheck
481
+ />
482
+ </FormField>
483
+ </Flex.Item>
484
+ <Flex.Item size="50%" padding="0 0 0 large">
485
+ <FormFieldGroup
486
+ description="Set value from the outside"
487
+ name="setValue"
488
+ >
489
+ <TextArea
490
+ label={
491
+ <ScreenReaderContent>Change value</ScreenReaderContent>
492
+ }
493
+ textareaRef={(e) => {
494
+ this.textAreaRef = e
495
+ }}
496
+ defaultValue={this.state.value}
497
+ />
498
+ <Button
499
+ color="primary"
500
+ onClick={() => {
501
+ this.setState({ value: this.textAreaRef.value })
502
+ }}
503
+ >
504
+ Update value
505
+ </Button>
506
+ </FormFieldGroup>
507
+ </Flex.Item>
508
+ </Flex>
509
+ </View>
510
+ )
511
+ }
275
512
  }
276
513
 
277
- textAreaRef = null
514
+ render(<ControlledExample />)
515
+ ```
516
+
517
+ - ```js
518
+ const ControlledExample = () => {
519
+ const [value, setValue] = useState(`const fruit: string = "apple"
520
+
521
+ function exampleMethod(props: Props) {
522
+ return props ? props.value : null
523
+ }`)
524
+
525
+ const textAreaRef = useRef(null)
278
526
 
279
- render () {
280
527
  return (
281
528
  <View display="block" background="primary">
282
- <Flex alignItems='start'>
529
+ <Flex alignItems="start">
283
530
  <Flex.Item shouldGrow shouldShrink padding="0 large 0 0">
284
531
  <FormField
285
- label='Controlled code editor'
286
- id='controlledCodeEditor'
287
- messages={[{
288
- type: 'hint',
289
- text: 'Type in the editor or set the value from the textarea.'
290
- }]}
532
+ label="Controlled code editor"
533
+ id="controlledCodeEditor"
534
+ messages={[
535
+ {
536
+ type: 'hint',
537
+ text: 'Type in the editor or set the value from the textarea.'
538
+ }
539
+ ]}
291
540
  >
292
541
  <SourceCodeEditor
293
- label='controlled code editor'
294
- value={this.state.value}
295
- onChange={(value) => {
296
- this.setState({ value })
297
- }}
542
+ label="controlled code editor"
543
+ value={value}
544
+ onChange={(newValue) => setValue(newValue)}
298
545
  highlightActiveLine
299
546
  highlightActiveLineGutter
300
547
  lineWrapping
@@ -304,19 +551,26 @@ function exampleMethod(props: Props) {
304
551
  />
305
552
  </FormField>
306
553
  </Flex.Item>
307
- <Flex.Item size='50%' padding="0 0 0 large">
554
+ <Flex.Item size="50%" padding="0 0 0 large">
308
555
  <FormFieldGroup
309
- description='Set value from the outside'
310
- name='setValue'
556
+ description="Set value from the outside"
557
+ name="setValue"
311
558
  >
312
559
  <TextArea
313
560
  label={<ScreenReaderContent>Change value</ScreenReaderContent>}
314
- textareaRef={(e) => { this.textAreaRef = e }}
315
- defaultValue={this.state.value}
561
+ textareaRef={(e) => {
562
+ textAreaRef.current = e
563
+ }}
564
+ defaultValue={value}
316
565
  />
317
- <Button color='primary' onClick={() => {
318
- this.setState({ value: this.textAreaRef.value })
319
- }}>
566
+ <Button
567
+ color="primary"
568
+ onClick={() => {
569
+ if (textAreaRef.current) {
570
+ setValue(textAreaRef.current.value)
571
+ }
572
+ }}
573
+ >
320
574
  Update value
321
575
  </Button>
322
576
  </FormFieldGroup>
@@ -325,10 +579,8 @@ function exampleMethod(props: Props) {
325
579
  </View>
326
580
  )
327
581
  }
328
- }
329
-
330
- render(<ControlledExample />)
331
- ```
582
+ render(<ControlledExample />)
583
+ ```
332
584
 
333
585
  ### Editable and readOnly
334
586
 
@@ -337,50 +589,93 @@ The editability of the content can be set with the combination of the `editable`
337
589
  The `readOnly` prop works like a "preventDefault" and disables any interaction by the user or API calls (e.g. copy-paste).
338
590
  If the `editable` prop is set to `false`, the editor is also not focusable, and the `contenteditable="false"` is set on the content.
339
591
 
340
- ```js
341
- ---
342
- type: example
343
- ---
344
- class EditableExample extends React.Component {
345
- state = {
346
- editable: true,
347
- readOnly: false
592
+ - ```js
593
+ class EditableExample extends React.Component {
594
+ state = {
595
+ editable: true,
596
+ readOnly: false
597
+ }
598
+
599
+ render() {
600
+ return (
601
+ <View
602
+ display="block"
603
+ padding="medium medium small"
604
+ background="primary"
605
+ >
606
+ <View display="block" margin="small none large">
607
+ <FormFieldGroup description="Settings" rowSpacing="small">
608
+ {['editable', 'readOnly'].map((prop) => (
609
+ <Checkbox
610
+ label={prop}
611
+ key={prop}
612
+ defaultChecked={this.state[prop]}
613
+ onChange={() => {
614
+ this.setState({ [prop]: !this.state[prop] })
615
+ }}
616
+ />
617
+ ))}
618
+ </FormFieldGroup>
619
+ </View>
620
+
621
+ <SourceCodeEditor
622
+ label="editable code editor"
623
+ language="jsx"
624
+ editable={this.state.editable}
625
+ readOnly={this.state.readOnly}
626
+ defaultValue={`function example() {
627
+ console.log('example')
628
+ }`}
629
+ />
630
+ </View>
631
+ )
632
+ }
348
633
  }
349
634
 
350
- render () {
635
+ render(<EditableExample />)
636
+ ```
637
+
638
+ - ```js
639
+ const EditableExample = () => {
640
+ const [settings, setSettings] = useState({
641
+ editable: true,
642
+ readOnly: false
643
+ })
644
+
351
645
  return (
352
646
  <View display="block" padding="medium medium small" background="primary">
353
647
  <View display="block" margin="small none large">
354
648
  <FormFieldGroup description="Settings" rowSpacing="small">
355
- {['editable', 'readOnly'].map((prop) => (
649
+ {Object.keys(settings).map((prop) => (
356
650
  <Checkbox
357
651
  label={prop}
358
652
  key={prop}
359
- defaultChecked={this.state[prop]}
360
- onChange={() => {
361
- this.setState({ [prop]: !this.state[prop] })
362
- }}
653
+ defaultChecked={settings[prop]}
654
+ onChange={() =>
655
+ setSettings((prevState) => ({
656
+ ...prevState,
657
+ [prop]: !prevState[prop]
658
+ }))
659
+ }
363
660
  />
364
661
  ))}
365
662
  </FormFieldGroup>
366
663
  </View>
367
664
 
368
665
  <SourceCodeEditor
369
- label='editable code editor'
666
+ label="editable code editor"
370
667
  language="jsx"
371
- editable={this.state.editable}
372
- readOnly={this.state.readOnly}
668
+ editable={settings.editable}
669
+ readOnly={settings.readOnly}
373
670
  defaultValue={`function example() {
374
- console.log('example')
375
- }`}
671
+ console.log('example')
672
+ }`}
376
673
  />
377
674
  </View>
378
675
  )
379
676
  }
380
- }
381
-
382
- render(<EditableExample />)
383
- ```
677
+ render(<EditableExample />)
678
+ ```
384
679
 
385
680
  ### Gutter settings
386
681
 
@@ -388,30 +683,113 @@ The `lineNumbers` prop displays the line numbers in the side gutter, and the `fo
388
683
 
389
684
  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.)
390
685
 
391
- ```js
392
- ---
393
- type: example
394
- ---
395
- class GutterExample extends React.Component {
396
- state = {
397
- lineNumbers: true,
398
- foldGutter: true,
399
- highlightActiveLineGutter: true,
400
- highlightActiveLine: true,
686
+ - ```js
687
+ class GutterExample extends React.Component {
688
+ state = {
689
+ lineNumbers: true,
690
+ foldGutter: true,
691
+ highlightActiveLineGutter: true,
692
+ highlightActiveLine: true
693
+ }
694
+
695
+ render() {
696
+ return (
697
+ <View
698
+ display="block"
699
+ padding="medium medium small"
700
+ background="primary"
701
+ >
702
+ <View display="block" margin="small none large">
703
+ <FormFieldGroup description="Settings" rowSpacing="small">
704
+ {[
705
+ 'lineNumbers',
706
+ 'foldGutter',
707
+ 'highlightActiveLineGutter',
708
+ 'highlightActiveLine'
709
+ ].map((prop) => (
710
+ <Checkbox
711
+ label={prop}
712
+ key={prop}
713
+ defaultChecked={this.state[prop]}
714
+ onChange={() => {
715
+ this.setState({ [prop]: !this.state[prop] })
716
+ }}
717
+ />
718
+ ))}
719
+ </FormFieldGroup>
720
+ </View>
721
+
722
+ <SourceCodeEditor
723
+ label="gutter example"
724
+ language="jsx"
725
+ lineNumbers={this.state.lineNumbers}
726
+ foldGutter={this.state.foldGutter}
727
+ highlightActiveLineGutter={this.state.highlightActiveLineGutter}
728
+ highlightActiveLine={this.state.highlightActiveLine}
729
+ defaultValue={`const fruit: string = "apple"
730
+
731
+ function exampleMethod(props: Props) {
732
+ return props ? props.value : null
401
733
  }
734
+
735
+ /**
736
+ * This is an example
737
+ * @param {Object} props
738
+ */
739
+ const Example = () => {
740
+ return (
741
+ <View as="div" padding={'large'}>
742
+ <Position
743
+ renderTarget={<GoodComponent />}
744
+ placement='end center'
745
+ offsetX='20px'
746
+ >
747
+ <span style={{ padding: '8px', background: 'white' }}>
748
+ Positioned content
749
+ </span>
750
+ </Position>
751
+ </View>
752
+ )
753
+ }
754
+
755
+ render(<Example />)`}
756
+ />
757
+ </View>
758
+ )
759
+ }
760
+ }
761
+
762
+ render(<GutterExample />)
763
+ ```
764
+
765
+ - ```js
766
+ const GutterExample = () => {
767
+ const [settings, setSettings] = useState({
768
+ lineNumbers: true,
769
+ foldGutter: true,
770
+ highlightActiveLineGutter: true,
771
+ highlightActiveLine: true
772
+ })
402
773
 
403
- render () {
404
774
  return (
405
775
  <View display="block" padding="medium medium small" background="primary">
406
776
  <View display="block" margin="small none large">
407
777
  <FormFieldGroup description="Settings" rowSpacing="small">
408
- {['lineNumbers', 'foldGutter', 'highlightActiveLineGutter', 'highlightActiveLine'].map((prop) => (
778
+ {[
779
+ 'lineNumbers',
780
+ 'foldGutter',
781
+ 'highlightActiveLineGutter',
782
+ 'highlightActiveLine'
783
+ ].map((prop) => (
409
784
  <Checkbox
410
785
  label={prop}
411
786
  key={prop}
412
- defaultChecked={this.state[prop]}
787
+ defaultChecked={settings[prop]}
413
788
  onChange={() => {
414
- this.setState({ [prop]: !this.state[prop] })
789
+ setSettings((prevSettings) => ({
790
+ ...prevSettings,
791
+ [prop]: !prevSettings[prop]
792
+ }))
415
793
  }}
416
794
  />
417
795
  ))}
@@ -419,23 +797,23 @@ class GutterExample extends React.Component {
419
797
  </View>
420
798
 
421
799
  <SourceCodeEditor
422
- label='gutter example'
800
+ label="gutter example"
423
801
  language="jsx"
424
- lineNumbers={this.state.lineNumbers}
425
- foldGutter={this.state.foldGutter}
426
- highlightActiveLineGutter={this.state.highlightActiveLineGutter}
427
- highlightActiveLine={this.state.highlightActiveLine}
802
+ lineNumbers={settings.lineNumbers}
803
+ foldGutter={settings.foldGutter}
804
+ highlightActiveLineGutter={settings.highlightActiveLineGutter}
805
+ highlightActiveLine={settings.highlightActiveLine}
428
806
  defaultValue={`const fruit: string = "apple"
429
-
430
- function exampleMethod(props: Props) {
807
+
808
+ function exampleMethod(props: Props) {
431
809
  return props ? props.value : null
432
- }
433
-
434
- /**
435
- * This is an example
436
- * @param {Object} props
437
- */
438
- const Example = () => {
810
+ }
811
+
812
+ /**
813
+ * This is an example
814
+ * @param {Object} props
815
+ */
816
+ const Example = () => {
439
817
  return (
440
818
  <View as="div" padding={'large'}>
441
819
  <Position
@@ -449,17 +827,15 @@ const Example = () => {
449
827
  </Position>
450
828
  </View>
451
829
  )
452
- }
453
-
454
- render(<Example />)`}
830
+ }
831
+
832
+ render(<Example />)`}
455
833
  />
456
834
  </View>
457
835
  )
458
836
  }
459
- }
460
-
461
- render(<GutterExample />)
462
- ```
837
+ render(<GutterExample />)
838
+ ```
463
839
 
464
840
  ### Indentation
465
841
 
@@ -483,107 +859,207 @@ The `indentUnitCount` prop should be a string consisting either entirely of spac
483
859
 
484
860
  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.
485
861
 
486
- ```js
487
- ---
488
- type: example
489
- ---
490
- class IndentExample extends React.Component {
491
- state = {
492
- indentWithTab: true,
493
- indentUnitCount: '2',
494
- }
862
+ - ```js
863
+ class IndentExample extends React.Component {
864
+ state = {
865
+ indentWithTab: true,
866
+ indentUnitCount: '2'
867
+ }
495
868
 
496
- editor = null
869
+ editor = null
497
870
 
498
- get indentUnit() {
499
- return Array(parseInt(this.state.indentUnitCount)).fill(' ').join('')
500
- }
871
+ get indentUnit() {
872
+ return Array(parseInt(this.state.indentUnitCount)).fill(' ').join('')
873
+ }
501
874
 
502
- reIndent () {
503
- this.editor.indentAll()
504
- }
875
+ reIndent() {
876
+ this.editor.indentAll()
877
+ }
878
+
879
+ indentCurrentSelection() {
880
+ this.editor.indentCurrentSelection()
881
+ }
882
+
883
+ render() {
884
+ return (
885
+ <View
886
+ display="block"
887
+ padding="medium medium small"
888
+ background="primary"
889
+ >
890
+ <View display="block" margin="small none large">
891
+ <FormFieldGroup description="Settings">
892
+ <Checkbox
893
+ label="indentWithTab"
894
+ defaultChecked={this.state.indentWithTab}
895
+ onChange={() => {
896
+ this.setState({ indentWithTab: !this.state.indentWithTab })
897
+ }}
898
+ />
899
+ <RadioInputGroup
900
+ name="indentUnitCount"
901
+ value={this.state.indentUnitCount}
902
+ description="indent space count"
903
+ onChange={(e, indentUnitCount) => {
904
+ this.setState({ indentUnitCount })
905
+ this.reIndent()
906
+ }}
907
+ >
908
+ {['2', '4', '8'].map((count) => (
909
+ <RadioInput key={count} label={count} value={count} />
910
+ ))}
911
+ </RadioInputGroup>
912
+ <Button
913
+ onClick={() => {
914
+ this.reIndent()
915
+ }}
916
+ >
917
+ Re-indent code
918
+ </Button>
919
+ <Button
920
+ onClick={() => {
921
+ this.indentCurrentSelection()
922
+ }}
923
+ >
924
+ Indent current selection
925
+ </Button>
926
+ </FormFieldGroup>
927
+ </View>
505
928
 
506
- indentCurrentSelection () {
507
- this.editor.indentCurrentSelection()
929
+ <SourceCodeEditor
930
+ label="indent example"
931
+ ref={(component) => {
932
+ this.editor = component
933
+ }}
934
+ language="jsx"
935
+ indentWithTab={this.state.indentWithTab}
936
+ indentUnit={this.indentUnit}
937
+ defaultValue={`const fruit: string = "apple"
938
+
939
+ function exampleMethod(props: Props) {
940
+ return props ? props.value : null
508
941
  }
942
+
943
+ /**
944
+ * This is an example
945
+ * @param {Object} props
946
+ */
947
+ const Example = () => {
948
+ return (
949
+ <View as="div" padding={'large'}>
950
+ <Position
951
+ renderTarget={<GoodComponent />}
952
+ placement='end center'
953
+ offsetX='20px'
954
+ >
955
+ <span style={{ padding: '8px', background: 'white' }}>
956
+ Positioned content
957
+ </span>
958
+ </Position>
959
+ </View>
960
+ )
961
+ }
962
+
963
+ render(<Example />)`}
964
+ />
965
+ </View>
966
+ )
967
+ }
968
+ }
969
+
970
+ render(<IndentExample />)
971
+ ```
972
+
973
+ - ```js
974
+ const IndentExample = () => {
975
+ const [indentWithTab, setIndentWithTab] = useState(true)
976
+ const [indentUnitCount, setIndentUnitCount] = useState('2')
977
+
978
+ const editorRef = useRef(null)
979
+
980
+ const indentUnit = Array(parseInt(indentUnitCount)).fill(' ').join('')
981
+
982
+ const reIndent = () => {
983
+ if (editorRef.current) {
984
+ editorRef.current.indentAll()
985
+ }
986
+ }
987
+
988
+ const indentCurrentSelection = () => {
989
+ if (editorRef.current) {
990
+ editorRef.current.indentCurrentSelection()
991
+ }
992
+ }
509
993
 
510
- render () {
511
994
  return (
512
995
  <View display="block" padding="medium medium small" background="primary">
513
996
  <View display="block" margin="small none large">
514
997
  <FormFieldGroup description="Settings">
515
998
  <Checkbox
516
999
  label="indentWithTab"
517
- defaultChecked={this.state.indentWithTab}
518
- onChange={() => {
519
- this.setState({ indentWithTab: !this.state.indentWithTab })
520
- }}
1000
+ defaultChecked={indentWithTab}
1001
+ onChange={() => setIndentWithTab(!indentWithTab)}
521
1002
  />
522
1003
  <RadioInputGroup
523
1004
  name="indentUnitCount"
524
- value={this.state.indentUnitCount}
1005
+ value={indentUnitCount}
525
1006
  description="indent space count"
526
- onChange={(e, indentUnitCount) => {
527
- this.setState({indentUnitCount})
528
- this.reIndent()
1007
+ onChange={(e, value) => {
1008
+ setIndentUnitCount(value)
1009
+ reIndent()
529
1010
  }}
530
1011
  >
531
- {['2', '4', '8'].map(count => <RadioInput key={count} label={count} value={count} />)}
1012
+ {['2', '4', '8'].map((count) => (
1013
+ <RadioInput key={count} label={count} value={count} />
1014
+ ))}
532
1015
  </RadioInputGroup>
533
- <Button onClick={() => {
534
- this.reIndent()
535
- }}>
536
- Re-indent code
537
- </Button>
538
- <Button onClick={() => {
539
- this.indentCurrentSelection()
540
- }}>
1016
+ <Button onClick={reIndent}>Re-indent code</Button>
1017
+ <Button onClick={indentCurrentSelection}>
541
1018
  Indent current selection
542
1019
  </Button>
543
1020
  </FormFieldGroup>
544
1021
  </View>
545
1022
 
546
1023
  <SourceCodeEditor
547
- label='indent example'
548
- ref={(component) => { this.editor = component }}
1024
+ label="indent example"
1025
+ ref={editorRef}
549
1026
  language="jsx"
550
- indentWithTab={this.state.indentWithTab}
551
- indentUnit={this.indentUnit}
1027
+ indentWithTab={indentWithTab}
1028
+ indentUnit={indentUnit}
552
1029
  defaultValue={`const fruit: string = "apple"
553
-
554
- function exampleMethod(props: Props) {
555
- return props ? props.value : null
556
- }
557
-
558
- /**
559
- * This is an example
560
- * @param {Object} props
561
- */
562
- const Example = () => {
563
- return (
564
- <View as="div" padding={'large'}>
565
- <Position
566
- renderTarget={<GoodComponent />}
567
- placement='end center'
568
- offsetX='20px'
569
- >
570
- <span style={{ padding: '8px', background: 'white' }}>
571
- Positioned content
572
- </span>
573
- </Position>
574
- </View>
575
- )
576
- }
577
-
578
- render(<Example />)`}
1030
+
1031
+ function exampleMethod(props: Props) {
1032
+ return props ? props.value : null
1033
+ }
1034
+
1035
+ /**
1036
+ * This is an example
1037
+ * @param {Object} props
1038
+ */
1039
+ const Example = () => {
1040
+ return (
1041
+ <View as="div" padding={'large'}>
1042
+ <Position
1043
+ renderTarget={<GoodComponent />}
1044
+ placement='end center'
1045
+ offsetX='20px'
1046
+ >
1047
+ <span style={{ padding: '8px', background: 'white' }}>
1048
+ Positioned content
1049
+ </span>
1050
+ </Position>
1051
+ </View>
1052
+ )
1053
+ }
1054
+
1055
+ render(<Example />)`}
579
1056
  />
580
1057
  </View>
581
1058
  )
582
1059
  }
583
- }
584
1060
 
585
- render(<IndentExample />)
586
- ```
1061
+ render(<IndentExample />)
1062
+ ```
587
1063
 
588
1064
  ### Direction
589
1065
 
@@ -591,83 +1067,176 @@ SourceCodeEditor is a bidirectional component. It will inherit the text-directio
591
1067
 
592
1068
  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).
593
1069
 
594
- ```js
595
- ---
596
- type: example
597
- ---
598
- class DirectionExample extends React.Component {
599
- state = {
600
- contextDir: 'unset',
601
- editorDir: 'unset',
602
- rtlMoveVisually: true,
1070
+ - ```js
1071
+ class DirectionExample extends React.Component {
1072
+ state = {
1073
+ contextDir: 'unset',
1074
+ editorDir: 'unset',
1075
+ rtlMoveVisually: true
1076
+ }
1077
+
1078
+ render() {
1079
+ return (
1080
+ <InstUISettingsProvider
1081
+ dir={
1082
+ this.state.contextDir !== 'unset'
1083
+ ? this.state.contextDir
1084
+ : undefined
1085
+ }
1086
+ >
1087
+ <View
1088
+ display="block"
1089
+ padding="medium medium small"
1090
+ background="primary"
1091
+ >
1092
+ <View display="block" margin="small none large">
1093
+ <FormFieldGroup
1094
+ description="Settings"
1095
+ layout="columns"
1096
+ vAlign="top"
1097
+ >
1098
+ <RadioInputGroup
1099
+ name="contextDir"
1100
+ value={this.state.contextDir}
1101
+ description="context direction"
1102
+ onChange={(e, contextDir) => {
1103
+ this.setState({ contextDir })
1104
+ }}
1105
+ >
1106
+ {['unset', 'ltr', 'rtl'].map((dir) => (
1107
+ <RadioInput key={dir} label={dir} value={dir} />
1108
+ ))}
1109
+ </RadioInputGroup>
1110
+ <RadioInputGroup
1111
+ name="editorDir"
1112
+ value={this.state.editorDir}
1113
+ description="editor direction"
1114
+ onChange={(e, editorDir) => {
1115
+ this.setState({ editorDir })
1116
+ }}
1117
+ >
1118
+ {['unset', 'ltr', 'rtl'].map((dir) => (
1119
+ <RadioInput key={dir} label={dir} value={dir} />
1120
+ ))}
1121
+ </RadioInputGroup>
1122
+ <Checkbox
1123
+ label="rtlMoveVisually"
1124
+ defaultChecked={this.state.rtlMoveVisually}
1125
+ onChange={() => {
1126
+ this.setState({
1127
+ rtlMoveVisually: !this.state.rtlMoveVisually
1128
+ })
1129
+ }}
1130
+ />
1131
+ </FormFieldGroup>
1132
+ </View>
1133
+
1134
+ <SourceCodeEditor
1135
+ label="editable code editor"
1136
+ language="jsx"
1137
+ direction={
1138
+ this.state.editorDir !== 'unset'
1139
+ ? this.state.editorDir
1140
+ : undefined
1141
+ }
1142
+ rtlMoveVisually={this.state.rtlMoveVisually}
1143
+ defaultValue={`function directionExample(dir?: 'ltr' | 'rtl') {
1144
+ console.log(dir)
1145
+ }`}
1146
+ />
1147
+ </View>
1148
+ </InstUISettingsProvider>
1149
+ )
1150
+ }
603
1151
  }
604
1152
 
605
- render () {
1153
+ render(<DirectionExample />)
1154
+ ```
1155
+
1156
+ - ```js
1157
+ const DirectionExample = () => {
1158
+ const [settings, setSettings] = useState({
1159
+ contextDir: 'unset',
1160
+ editorDir: 'unset',
1161
+ rtlMoveVisually: true
1162
+ })
1163
+
606
1164
  return (
607
- <InstUISettingsProvider dir={this.state.contextDir !== 'unset'
608
- ? this.state.contextDir
609
- : undefined
610
- }>
1165
+ <InstUISettingsProvider
1166
+ dir={settings.contextDir !== 'unset' ? settings.contextDir : undefined}
1167
+ >
611
1168
  <View
612
1169
  display="block"
613
1170
  padding="medium medium small"
614
1171
  background="primary"
615
1172
  >
616
- <View
617
- display="block"
618
- margin="small none large"
619
- >
620
- <FormFieldGroup description="Settings" layout='columns' vAlign='top'>
1173
+ <View display="block" margin="small none large">
1174
+ <FormFieldGroup
1175
+ description="Settings"
1176
+ layout="columns"
1177
+ vAlign="top"
1178
+ >
621
1179
  <RadioInputGroup
622
1180
  name="contextDir"
623
- value={this.state.contextDir}
1181
+ value={settings.contextDir}
624
1182
  description="context direction"
625
1183
  onChange={(e, contextDir) => {
626
- this.setState({contextDir})
1184
+ setSettings((prevSettings) => ({
1185
+ ...prevSettings,
1186
+ contextDir
1187
+ }))
627
1188
  }}
628
1189
  >
629
- {['unset', 'ltr', 'rtl'].map(dir => <RadioInput key={dir} label={dir} value={dir} />)}
1190
+ {['unset', 'ltr', 'rtl'].map((dir) => (
1191
+ <RadioInput key={dir} label={dir} value={dir} />
1192
+ ))}
630
1193
  </RadioInputGroup>
631
1194
  <RadioInputGroup
632
1195
  name="editorDir"
633
- value={this.state.editorDir}
1196
+ value={settings.editorDir}
634
1197
  description="editor direction"
635
1198
  onChange={(e, editorDir) => {
636
- this.setState({editorDir})
1199
+ setSettings((prevSettings) => ({
1200
+ ...prevSettings,
1201
+ editorDir
1202
+ }))
637
1203
  }}
638
1204
  >
639
- {['unset', 'ltr', 'rtl'].map(dir => <RadioInput key={dir} label={dir} value={dir} />)}
1205
+ {['unset', 'ltr', 'rtl'].map((dir) => (
1206
+ <RadioInput key={dir} label={dir} value={dir} />
1207
+ ))}
640
1208
  </RadioInputGroup>
641
1209
  <Checkbox
642
1210
  label="rtlMoveVisually"
643
- defaultChecked={this.state.rtlMoveVisually}
1211
+ defaultChecked={settings.rtlMoveVisually}
644
1212
  onChange={() => {
645
- this.setState({ rtlMoveVisually: !this.state.rtlMoveVisually })
1213
+ setSettings((prevSettings) => ({
1214
+ ...prevSettings,
1215
+ rtlMoveVisually: !prevSettings.rtlMoveVisually
1216
+ }))
646
1217
  }}
647
1218
  />
648
1219
  </FormFieldGroup>
649
1220
  </View>
650
1221
 
651
1222
  <SourceCodeEditor
652
- label='editable code editor'
1223
+ label="editable code editor"
653
1224
  language="jsx"
654
- direction={this.state.editorDir !== 'unset'
655
- ? this.state.editorDir
656
- : undefined
1225
+ direction={
1226
+ settings.editorDir !== 'unset' ? settings.editorDir : undefined
657
1227
  }
658
- rtlMoveVisually={this.state.rtlMoveVisually}
1228
+ rtlMoveVisually={settings.rtlMoveVisually}
659
1229
  defaultValue={`function directionExample(dir?: 'ltr' | 'rtl') {
660
- console.log(dir)
661
- }`}
1230
+ console.log(dir)
1231
+ }`}
662
1232
  />
663
1233
  </View>
664
1234
  </InstUISettingsProvider>
665
1235
  )
666
1236
  }
667
- }
668
1237
 
669
- render(<DirectionExample />)
670
- ```
1238
+ render(<DirectionExample />)
1239
+ ```
671
1240
 
672
1241
  ### Focus management
673
1242
 
@@ -677,68 +1246,180 @@ The `autofocus` prop will automatically focus the editor on the initial render.
677
1246
 
678
1247
  You can also manually focus the editor with its public `focus` method (the `hasFocus` getter is also available).
679
1248
 
680
- ```js
681
- ---
682
- type: example
683
- ---
684
- class FocusExample extends React.Component {
685
- state = {
686
- indentWithTab: true,
687
- indentUnitCount: '2',
1249
+ - ```js
1250
+ class FocusExample extends React.Component {
1251
+ state = {
1252
+ indentWithTab: true,
1253
+ indentUnitCount: '2'
1254
+ }
1255
+
1256
+ editor = null
1257
+
1258
+ render() {
1259
+ return (
1260
+ <View
1261
+ display="block"
1262
+ padding="medium medium small"
1263
+ background="primary"
1264
+ >
1265
+ <View display="block" margin="small none large">
1266
+ <Button
1267
+ onClick={() => {
1268
+ console.log('manual focus')
1269
+ this.editor.focus()
1270
+ }}
1271
+ >
1272
+ Focus editor
1273
+ </Button>
1274
+ </View>
1275
+
1276
+ <SourceCodeEditor
1277
+ label="focus example"
1278
+ ref={(component) => {
1279
+ this.editor = component
1280
+ }}
1281
+ language="jsx"
1282
+ onFocus={() => {
1283
+ console.log('onFocus')
1284
+ console.log({ hasFocus: this.editor.hasFocus })
1285
+ }}
1286
+ onBlur={() => {
1287
+ console.log('onBlur')
1288
+ console.log({ hasFocus: this.editor.hasFocus })
1289
+ }}
1290
+ defaultValue={`function exampleMethod(props: Props) {
1291
+ return props ? props.value : null
1292
+ }`}
1293
+ />
1294
+ </View>
1295
+ )
1296
+ }
688
1297
  }
689
1298
 
690
- editor = null
1299
+ render(<FocusExample />)
1300
+ ```
1301
+
1302
+ - ```js
1303
+ const FocusExample = () => {
1304
+ const [indentWithTab, setIndentWithTab] = useState(true)
1305
+ const [indentUnitCount, setIndentUnitCount] = useState('2')
1306
+
1307
+ const editorRef = useRef(null)
691
1308
 
692
- render () {
693
1309
  return (
694
1310
  <View display="block" padding="medium medium small" background="primary">
695
1311
  <View display="block" margin="small none large">
696
- <Button onClick={() => {
697
- console.log('manual focus')
698
- this.editor.focus()
699
- }}>
1312
+ <Button
1313
+ onClick={() => {
1314
+ console.log('manual focus')
1315
+ editorRef.current.focus()
1316
+ }}
1317
+ >
700
1318
  Focus editor
701
1319
  </Button>
702
1320
  </View>
703
1321
 
704
1322
  <SourceCodeEditor
705
- label='focus example'
706
- ref={(component) => { this.editor = component }}
1323
+ label="focus example"
1324
+ ref={editorRef}
707
1325
  language="jsx"
708
1326
  onFocus={() => {
709
1327
  console.log('onFocus')
710
- console.log({ hasFocus: this.editor.hasFocus })
1328
+ console.log({ hasFocus: editorRef.current.hasFocus })
711
1329
  }}
712
1330
  onBlur={() => {
713
1331
  console.log('onBlur')
714
- console.log({ hasFocus: this.editor.hasFocus })
1332
+ console.log({ hasFocus: editorRef.current.hasFocus })
715
1333
  }}
716
1334
  defaultValue={`function exampleMethod(props: Props) {
717
- return props ? props.value : null
718
- }`}
1335
+ return props ? props.value : null
1336
+ }`}
719
1337
  />
720
1338
  </View>
721
1339
  )
722
1340
  }
723
- }
724
1341
 
725
- render(<FocusExample />)
726
- ```
1342
+ render(<FocusExample />)
1343
+ ```
727
1344
 
728
1345
  ### Attachment
729
1346
 
730
1347
  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.
731
1348
 
732
- ```js
733
- ---
734
- type: example
735
- ---
736
- class AttachmentExample extends React.Component {
737
- state = {
738
- attachment: 'none'
1349
+ - ```js
1350
+ class AttachmentExample extends React.Component {
1351
+ state = {
1352
+ attachment: 'none'
1353
+ }
1354
+
1355
+ render() {
1356
+ const viewProps = {
1357
+ as: 'div',
1358
+ background: 'primary-inverse',
1359
+ padding: 'small'
1360
+ }
1361
+
1362
+ return (
1363
+ <View
1364
+ display="block"
1365
+ padding="medium medium small"
1366
+ background="primary"
1367
+ >
1368
+ <View display="block" margin="small none large">
1369
+ <RadioInputGroup
1370
+ name="attachmentExample"
1371
+ value={this.state.attachment}
1372
+ description="attachment"
1373
+ onChange={(e, attachment) => {
1374
+ this.setState({ attachment })
1375
+ }}
1376
+ >
1377
+ {['none', 'top', 'bottom'].map((attachment) => (
1378
+ <RadioInput
1379
+ key={attachment}
1380
+ label={attachment}
1381
+ value={attachment}
1382
+ />
1383
+ ))}
1384
+ </RadioInputGroup>
1385
+ </View>
1386
+
1387
+ {this.state.attachment === 'bottom' && (
1388
+ <View {...viewProps}>
1389
+ CodeEditor is attached to the bottom of this element
1390
+ </View>
1391
+ )}
1392
+ <SourceCodeEditor
1393
+ label="attachment example"
1394
+ language="jsx"
1395
+ attachment={
1396
+ this.state.attachment === 'none'
1397
+ ? undefined
1398
+ : this.state.attachment
1399
+ }
1400
+ defaultValue={`const fruit: string = "apple"
1401
+
1402
+ function exampleMethod(props: Props) {
1403
+ return props ? props.value : null
1404
+ }`}
1405
+ />
1406
+ {this.state.attachment === 'top' && (
1407
+ <View {...viewProps}>
1408
+ CodeEditor is attached to the top of this element
1409
+ </View>
1410
+ )}
1411
+ </View>
1412
+ )
1413
+ }
739
1414
  }
740
1415
 
741
- render () {
1416
+ render(<AttachmentExample />)
1417
+ ```
1418
+
1419
+ - ```js
1420
+ const AttachmentExample = () => {
1421
+ const [attachment, setAttachment] = useState('none')
1422
+
742
1423
  const viewProps = {
743
1424
  as: 'div',
744
1425
  background: 'primary-inverse',
@@ -750,32 +1431,38 @@ class AttachmentExample extends React.Component {
750
1431
  <View display="block" margin="small none large">
751
1432
  <RadioInputGroup
752
1433
  name="attachmentExample"
753
- value={this.state.attachment}
1434
+ value={attachment}
754
1435
  description="attachment"
755
1436
  onChange={(e, attachment) => {
756
- this.setState({attachment})
1437
+ setAttachment(attachment)
757
1438
  }}
758
1439
  >
759
- {['none', 'top', 'bottom'].map(attachment => <RadioInput key={attachment} label={attachment} value={attachment} />)}
1440
+ {['none', 'top', 'bottom'].map((attachment) => (
1441
+ <RadioInput
1442
+ key={attachment}
1443
+ label={attachment}
1444
+ value={attachment}
1445
+ />
1446
+ ))}
760
1447
  </RadioInputGroup>
761
1448
  </View>
762
1449
 
763
- {this.state.attachment === 'bottom' && (
1450
+ {attachment === 'bottom' && (
764
1451
  <View {...viewProps}>
765
1452
  CodeEditor is attached to the bottom of this element
766
1453
  </View>
767
1454
  )}
768
1455
  <SourceCodeEditor
769
- label='attachment example'
1456
+ label="attachment example"
770
1457
  language="jsx"
771
- attachment={this.state.attachment === 'none' ? undefined : this.state.attachment}
1458
+ attachment={attachment === 'none' ? undefined : attachment}
772
1459
  defaultValue={`const fruit: string = "apple"
773
-
774
- function exampleMethod(props: Props) {
775
- return props ? props.value : null
776
- }`}
1460
+
1461
+ function exampleMethod(props: Props) {
1462
+ return props ? props.value : null
1463
+ }`}
777
1464
  />
778
- {this.state.attachment === 'top' && (
1465
+ {attachment === 'top' && (
779
1466
  <View {...viewProps}>
780
1467
  CodeEditor is attached to the top of this element
781
1468
  </View>
@@ -783,10 +1470,9 @@ function exampleMethod(props: Props) {
783
1470
  </View>
784
1471
  )
785
1472
  }
786
- }
787
1473
 
788
- render(<AttachmentExample />)
789
- ```
1474
+ render(<AttachmentExample />)
1475
+ ```
790
1476
 
791
1477
  ### Search
792
1478