@instructure/ui-source-code-editor 11.7.4-snapshot-80 → 11.7.4-snapshot-98
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 +5 -2
- package/es/SourceCodeEditor/v2/SearchPanel.js +14 -9
- package/es/SourceCodeEditor/v2/index.js +39 -6
- package/es/SourceCodeEditor/v2/styles.js +4 -0
- package/lib/SourceCodeEditor/v2/SearchPanel.js +14 -8
- package/lib/SourceCodeEditor/v2/index.js +40 -6
- package/lib/SourceCodeEditor/v2/styles.js +4 -0
- package/package.json +13 -13
- package/src/SourceCodeEditor/v2/SearchPanel.tsx +25 -6
- package/src/SourceCodeEditor/v2/index.tsx +54 -5
- package/src/SourceCodeEditor/v2/styles.ts +4 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/types/SourceCodeEditor/v2/SearchPanel.d.ts +15 -1
- package/types/SourceCodeEditor/v2/SearchPanel.d.ts.map +1 -1
- package/types/SourceCodeEditor/v2/index.d.ts +15 -1
- package/types/SourceCodeEditor/v2/index.d.ts.map +1 -1
- package/types/SourceCodeEditor/v2/styles.d.ts.map +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
-
## [11.7.4-snapshot-
|
|
6
|
+
## [11.7.4-snapshot-98](https://github.com/instructure/instructure-ui/compare/v11.7.3...v11.7.4-snapshot-98) (2026-07-14)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* **ui-source-code-editor:** apply current theme to v2 search panel ([4534fa0](https://github.com/instructure/instructure-ui/commit/4534fa0fc244bfb7138f3d09fbeeb1e73dfa6afd))
|
|
9
12
|
|
|
10
13
|
|
|
11
14
|
|
|
@@ -27,9 +27,13 @@ import { setSearchQuery, search, findNext, findPrevious, SearchQuery, closeSearc
|
|
|
27
27
|
import { TextInput } from '@instructure/ui-text-input/latest';
|
|
28
28
|
import { IconButton } from '@instructure/ui-buttons/latest';
|
|
29
29
|
import { IconArrowOpenDownLine, IconArrowOpenUpLine, IconSearchLine } from '@instructure/ui-icons';
|
|
30
|
-
import { createRoot } from 'react-dom/client';
|
|
31
30
|
import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
|
|
32
|
-
|
|
31
|
+
/**
|
|
32
|
+
* ---
|
|
33
|
+
* private: true
|
|
34
|
+
* ---
|
|
35
|
+
*/
|
|
36
|
+
export function SearchPanel({
|
|
33
37
|
view,
|
|
34
38
|
searchConfig
|
|
35
39
|
}) {
|
|
@@ -95,18 +99,19 @@ function SearchPanel({
|
|
|
95
99
|
})
|
|
96
100
|
});
|
|
97
101
|
}
|
|
98
|
-
export default function customSearch(searchConfig) {
|
|
102
|
+
export default function customSearch(searchConfig, handlers) {
|
|
99
103
|
return searchConfig ? search({
|
|
100
104
|
createPanel: view => {
|
|
101
105
|
const dom = document.createElement('div');
|
|
102
106
|
dom.style.padding = '8px';
|
|
103
|
-
const root = createRoot(dom);
|
|
104
|
-
root.render(_jsx(SearchPanel, {
|
|
105
|
-
view: view,
|
|
106
|
-
searchConfig: searchConfig
|
|
107
|
-
}));
|
|
108
107
|
return {
|
|
109
|
-
dom
|
|
108
|
+
dom,
|
|
109
|
+
mount() {
|
|
110
|
+
handlers.onMount(dom, view);
|
|
111
|
+
},
|
|
112
|
+
destroy() {
|
|
113
|
+
handlers.onDestroy(dom);
|
|
114
|
+
}
|
|
110
115
|
};
|
|
111
116
|
}
|
|
112
117
|
}) : [];
|
|
@@ -24,6 +24,7 @@ var _dec, _dec2, _dec3, _class;
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
import { Component } from 'react';
|
|
27
|
+
import { createPortal } from 'react-dom';
|
|
27
28
|
import { deepEqual as isEqual } from '@instructure/ui-utils';
|
|
28
29
|
import { EditorState, StateEffect } from '@codemirror/state';
|
|
29
30
|
import { EditorView, highlightSpecialChars, highlightActiveLine, drawSelection, dropCursor, rectangularSelection, crosshairCursor, lineNumbers, highlightActiveLineGutter, keymap } from '@codemirror/view';
|
|
@@ -46,7 +47,7 @@ import { requestAnimationFrame } from '@instructure/ui-dom-utils';
|
|
|
46
47
|
import { ScreenReaderContent } from '@instructure/ui-a11y-content';
|
|
47
48
|
import { textDirectionContextConsumer } from '@instructure/ui-i18n';
|
|
48
49
|
import { withStyleNew } from '@instructure/emotion';
|
|
49
|
-
import customSearch from './SearchPanel.js';
|
|
50
|
+
import customSearch, { SearchPanel } from './SearchPanel.js';
|
|
50
51
|
import generateStyle from './styles.js';
|
|
51
52
|
import { rtlHorizontalArrowKeymap } from './customKeybinding.js';
|
|
52
53
|
import { allowedProps } from './props.js';
|
|
@@ -79,10 +80,30 @@ let SourceCodeEditor = (_dec = withDeterministicId(), _dec2 = withStyleNew(gener
|
|
|
79
80
|
};
|
|
80
81
|
_id;
|
|
81
82
|
ref = null;
|
|
83
|
+
state = {
|
|
84
|
+
searchPanels: []
|
|
85
|
+
};
|
|
82
86
|
_containerRef;
|
|
83
87
|
_editorView;
|
|
84
88
|
_raf = [];
|
|
89
|
+
_searchPanelId = 0;
|
|
90
|
+
_isUnmounting = false;
|
|
85
91
|
_newSelectionAfterValueChange;
|
|
92
|
+
handleSearchPanelMount = (dom, view) => {
|
|
93
|
+
this.setState(state => ({
|
|
94
|
+
searchPanels: [...state.searchPanels, {
|
|
95
|
+
id: ++this._searchPanelId,
|
|
96
|
+
dom,
|
|
97
|
+
view
|
|
98
|
+
}]
|
|
99
|
+
}));
|
|
100
|
+
};
|
|
101
|
+
handleSearchPanelDestroy = dom => {
|
|
102
|
+
if (this._isUnmounting) return;
|
|
103
|
+
this.setState(state => ({
|
|
104
|
+
searchPanels: state.searchPanels.filter(panel => panel.dom !== dom)
|
|
105
|
+
}));
|
|
106
|
+
};
|
|
86
107
|
handleRef = el => {
|
|
87
108
|
const {
|
|
88
109
|
elementRef
|
|
@@ -245,6 +266,7 @@ let SourceCodeEditor = (_dec = withDeterministicId(), _dec2 = withStyleNew(gener
|
|
|
245
266
|
this.assignAriaLabel();
|
|
246
267
|
}
|
|
247
268
|
componentWillUnmount() {
|
|
269
|
+
this._isUnmounting = true;
|
|
248
270
|
this._editorView?.destroy();
|
|
249
271
|
this.cancelAnimationFrames();
|
|
250
272
|
}
|
|
@@ -262,7 +284,7 @@ let SourceCodeEditor = (_dec = withDeterministicId(), _dec2 = withStyleNew(gener
|
|
|
262
284
|
shouldUpdateExtensions(prevProps) {
|
|
263
285
|
const propsToObserve = ['styles',
|
|
264
286
|
// needed for theme update
|
|
265
|
-
'themeOverride', 'language', 'readOnly', 'editable', 'lineNumbers', 'highlightActiveLineGutter', 'foldGutter', 'lineWrapping', 'autofocus', 'spellcheck', 'direction', 'dir', 'rtlMoveVisually', 'indentOnLoad', 'indentWithTab', 'indentUnit', 'highlightActiveLine', 'attachment'];
|
|
287
|
+
'themeOverride', 'language', 'readOnly', 'editable', 'lineNumbers', 'highlightActiveLineGutter', 'foldGutter', 'lineWrapping', 'autofocus', 'spellcheck', 'direction', 'dir', 'rtlMoveVisually', 'indentOnLoad', 'indentWithTab', 'indentUnit', 'highlightActiveLine', 'attachment', 'searchConfig'];
|
|
266
288
|
for (const prop of propsToObserve) {
|
|
267
289
|
if (!isEqual(this.props[prop], prevProps[prop])) {
|
|
268
290
|
return true;
|
|
@@ -326,7 +348,10 @@ let SourceCodeEditor = (_dec = withDeterministicId(), _dec2 = withStyleNew(gener
|
|
|
326
348
|
// and adjust it as desired.
|
|
327
349
|
highlightSpecialChars(), history(), drawSelection(), dropCursor(), EditorState.allowMultipleSelections.of(true), syntaxHighlighting(defaultHighlightStyle, {
|
|
328
350
|
fallback: true
|
|
329
|
-
}), bracketMatching(), closeBrackets(), autocompletion(), rectangularSelection(), crosshairCursor(), highlightSelectionMatches(), indentOnInput(), customSearch(this.props.searchConfig
|
|
351
|
+
}), bracketMatching(), closeBrackets(), autocompletion(), rectangularSelection(), crosshairCursor(), highlightSelectionMatches(), indentOnInput(), customSearch(this.props.searchConfig, {
|
|
352
|
+
onMount: this.handleSearchPanelMount,
|
|
353
|
+
onDestroy: this.handleSearchPanelDestroy
|
|
354
|
+
}), keymap.of(this.keymaps)];
|
|
330
355
|
}
|
|
331
356
|
get keymaps() {
|
|
332
357
|
// TODO: if more keymaps are added, list them in the docs as well (#Command keybinding)
|
|
@@ -529,14 +554,15 @@ let SourceCodeEditor = (_dec = withDeterministicId(), _dec2 = withStyleNew(gener
|
|
|
529
554
|
const {
|
|
530
555
|
label,
|
|
531
556
|
styles,
|
|
557
|
+
searchConfig,
|
|
532
558
|
...restProps
|
|
533
559
|
} = this.props;
|
|
534
|
-
return
|
|
560
|
+
return _jsxs("div", {
|
|
535
561
|
"data-cid": "SourceCodeEditor",
|
|
536
562
|
ref: this.handleRef,
|
|
537
563
|
css: styles?.codeEditor,
|
|
538
564
|
...passthroughProps(omitProps(restProps, SourceCodeEditor.allowedProps)),
|
|
539
|
-
children: _jsxs("label", {
|
|
565
|
+
children: [_jsxs("label", {
|
|
540
566
|
css: styles?.label,
|
|
541
567
|
id: this._id,
|
|
542
568
|
children: [_jsx(ScreenReaderContent, {
|
|
@@ -545,7 +571,14 @@ let SourceCodeEditor = (_dec = withDeterministicId(), _dec2 = withStyleNew(gener
|
|
|
545
571
|
ref: this.handleContainerRef,
|
|
546
572
|
css: styles?.codeEditorContainer
|
|
547
573
|
})]
|
|
548
|
-
})
|
|
574
|
+
}), searchConfig && this.state.searchPanels.map(({
|
|
575
|
+
id,
|
|
576
|
+
dom,
|
|
577
|
+
view
|
|
578
|
+
}) => /*#__PURE__*/createPortal(_jsx(SearchPanel, {
|
|
579
|
+
view: view,
|
|
580
|
+
searchConfig: searchConfig
|
|
581
|
+
}), dom, String(id)))]
|
|
549
582
|
});
|
|
550
583
|
}
|
|
551
584
|
}) || _class) || _class) || _class);
|
|
@@ -144,6 +144,10 @@ const generateStyle = (componentTheme, props, sharedTokens) => {
|
|
|
144
144
|
'.cm-placeholder': {
|
|
145
145
|
// for better contrast
|
|
146
146
|
color: componentTheme.placeholderBackgroundColor
|
|
147
|
+
},
|
|
148
|
+
'.cm-panels': {
|
|
149
|
+
backgroundColor: componentTheme.background,
|
|
150
|
+
color: componentTheme.color
|
|
147
151
|
}
|
|
148
152
|
},
|
|
149
153
|
highlightStyle: [
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
+
exports.SearchPanel = SearchPanel;
|
|
6
7
|
exports.default = customSearch;
|
|
7
8
|
var _react = require("react");
|
|
8
9
|
var _search = require("@codemirror/search");
|
|
@@ -11,7 +12,6 @@ var _latest2 = require("@instructure/ui-buttons/latest");
|
|
|
11
12
|
var _IconArrowOpenDownLine = require("@instructure/ui-icons/lib/generated/IconArrowOpenDownLine.js");
|
|
12
13
|
var _IconArrowOpenUpLine = require("@instructure/ui-icons/lib/generated/IconArrowOpenUpLine.js");
|
|
13
14
|
var _IconSearchLine = require("@instructure/ui-icons/lib/generated/IconSearchLine.js");
|
|
14
|
-
var _client = require("react-dom/client");
|
|
15
15
|
var _jsxRuntime = require("@emotion/react/jsx-runtime");
|
|
16
16
|
/*
|
|
17
17
|
* The MIT License (MIT)
|
|
@@ -37,6 +37,11 @@ var _jsxRuntime = require("@emotion/react/jsx-runtime");
|
|
|
37
37
|
* SOFTWARE.
|
|
38
38
|
*/
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* ---
|
|
42
|
+
* private: true
|
|
43
|
+
* ---
|
|
44
|
+
*/
|
|
40
45
|
function SearchPanel({
|
|
41
46
|
view,
|
|
42
47
|
searchConfig
|
|
@@ -103,18 +108,19 @@ function SearchPanel({
|
|
|
103
108
|
})
|
|
104
109
|
});
|
|
105
110
|
}
|
|
106
|
-
function customSearch(searchConfig) {
|
|
111
|
+
function customSearch(searchConfig, handlers) {
|
|
107
112
|
return searchConfig ? (0, _search.search)({
|
|
108
113
|
createPanel: view => {
|
|
109
114
|
const dom = document.createElement('div');
|
|
110
115
|
dom.style.padding = '8px';
|
|
111
|
-
const root = (0, _client.createRoot)(dom);
|
|
112
|
-
root.render((0, _jsxRuntime.jsx)(SearchPanel, {
|
|
113
|
-
view: view,
|
|
114
|
-
searchConfig: searchConfig
|
|
115
|
-
}));
|
|
116
116
|
return {
|
|
117
|
-
dom
|
|
117
|
+
dom,
|
|
118
|
+
mount() {
|
|
119
|
+
handlers.onMount(dom, view);
|
|
120
|
+
},
|
|
121
|
+
destroy() {
|
|
122
|
+
handlers.onDestroy(dom);
|
|
123
|
+
}
|
|
118
124
|
};
|
|
119
125
|
}
|
|
120
126
|
}) : [];
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
|
|
3
4
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
|
|
4
5
|
Object.defineProperty(exports, "__esModule", {
|
|
5
6
|
value: true
|
|
6
7
|
});
|
|
7
8
|
exports.default = exports.SourceCodeEditor = void 0;
|
|
8
9
|
var _react = require("react");
|
|
10
|
+
var _reactDom = require("react-dom");
|
|
9
11
|
var _deepEqual = require("@instructure/ui-utils/lib/deepEqual.js");
|
|
10
12
|
var _state = require("@codemirror/state");
|
|
11
13
|
var _view = require("@codemirror/view");
|
|
@@ -28,7 +30,7 @@ var _requestAnimationFrame = require("@instructure/ui-dom-utils/lib/requestAnima
|
|
|
28
30
|
var _ScreenReaderContent = require("@instructure/ui-a11y-content/lib/ScreenReaderContent");
|
|
29
31
|
var _textDirectionContextConsumer = require("@instructure/ui-i18n/lib/textDirectionContextConsumer.js");
|
|
30
32
|
var _emotion = require("@instructure/emotion");
|
|
31
|
-
var _SearchPanel =
|
|
33
|
+
var _SearchPanel = _interopRequireWildcard(require("./SearchPanel.js"));
|
|
32
34
|
var _styles = _interopRequireDefault(require("./styles.js"));
|
|
33
35
|
var _customKeybinding = require("./customKeybinding.js");
|
|
34
36
|
var _props = require("./props.js");
|
|
@@ -86,10 +88,30 @@ let SourceCodeEditor = exports.SourceCodeEditor = (_dec = (0, _withDeterministic
|
|
|
86
88
|
};
|
|
87
89
|
_id;
|
|
88
90
|
ref = null;
|
|
91
|
+
state = {
|
|
92
|
+
searchPanels: []
|
|
93
|
+
};
|
|
89
94
|
_containerRef;
|
|
90
95
|
_editorView;
|
|
91
96
|
_raf = [];
|
|
97
|
+
_searchPanelId = 0;
|
|
98
|
+
_isUnmounting = false;
|
|
92
99
|
_newSelectionAfterValueChange;
|
|
100
|
+
handleSearchPanelMount = (dom, view) => {
|
|
101
|
+
this.setState(state => ({
|
|
102
|
+
searchPanels: [...state.searchPanels, {
|
|
103
|
+
id: ++this._searchPanelId,
|
|
104
|
+
dom,
|
|
105
|
+
view
|
|
106
|
+
}]
|
|
107
|
+
}));
|
|
108
|
+
};
|
|
109
|
+
handleSearchPanelDestroy = dom => {
|
|
110
|
+
if (this._isUnmounting) return;
|
|
111
|
+
this.setState(state => ({
|
|
112
|
+
searchPanels: state.searchPanels.filter(panel => panel.dom !== dom)
|
|
113
|
+
}));
|
|
114
|
+
};
|
|
93
115
|
handleRef = el => {
|
|
94
116
|
const {
|
|
95
117
|
elementRef
|
|
@@ -252,6 +274,7 @@ let SourceCodeEditor = exports.SourceCodeEditor = (_dec = (0, _withDeterministic
|
|
|
252
274
|
this.assignAriaLabel();
|
|
253
275
|
}
|
|
254
276
|
componentWillUnmount() {
|
|
277
|
+
this._isUnmounting = true;
|
|
255
278
|
this._editorView?.destroy();
|
|
256
279
|
this.cancelAnimationFrames();
|
|
257
280
|
}
|
|
@@ -269,7 +292,7 @@ let SourceCodeEditor = exports.SourceCodeEditor = (_dec = (0, _withDeterministic
|
|
|
269
292
|
shouldUpdateExtensions(prevProps) {
|
|
270
293
|
const propsToObserve = ['styles',
|
|
271
294
|
// needed for theme update
|
|
272
|
-
'themeOverride', 'language', 'readOnly', 'editable', 'lineNumbers', 'highlightActiveLineGutter', 'foldGutter', 'lineWrapping', 'autofocus', 'spellcheck', 'direction', 'dir', 'rtlMoveVisually', 'indentOnLoad', 'indentWithTab', 'indentUnit', 'highlightActiveLine', 'attachment'];
|
|
295
|
+
'themeOverride', 'language', 'readOnly', 'editable', 'lineNumbers', 'highlightActiveLineGutter', 'foldGutter', 'lineWrapping', 'autofocus', 'spellcheck', 'direction', 'dir', 'rtlMoveVisually', 'indentOnLoad', 'indentWithTab', 'indentUnit', 'highlightActiveLine', 'attachment', 'searchConfig'];
|
|
273
296
|
for (const prop of propsToObserve) {
|
|
274
297
|
if (!(0, _deepEqual.deepEqual)(this.props[prop], prevProps[prop])) {
|
|
275
298
|
return true;
|
|
@@ -333,7 +356,10 @@ let SourceCodeEditor = exports.SourceCodeEditor = (_dec = (0, _withDeterministic
|
|
|
333
356
|
// and adjust it as desired.
|
|
334
357
|
(0, _view.highlightSpecialChars)(), (0, _commands.history)(), (0, _view.drawSelection)(), (0, _view.dropCursor)(), _state.EditorState.allowMultipleSelections.of(true), (0, _language.syntaxHighlighting)(_language.defaultHighlightStyle, {
|
|
335
358
|
fallback: true
|
|
336
|
-
}), (0, _language.bracketMatching)(), (0, _autocomplete.closeBrackets)(), (0, _autocomplete.autocompletion)(), (0, _view.rectangularSelection)(), (0, _view.crosshairCursor)(), (0, _search.highlightSelectionMatches)(), (0, _language.indentOnInput)(), (0, _SearchPanel.default)(this.props.searchConfig
|
|
359
|
+
}), (0, _language.bracketMatching)(), (0, _autocomplete.closeBrackets)(), (0, _autocomplete.autocompletion)(), (0, _view.rectangularSelection)(), (0, _view.crosshairCursor)(), (0, _search.highlightSelectionMatches)(), (0, _language.indentOnInput)(), (0, _SearchPanel.default)(this.props.searchConfig, {
|
|
360
|
+
onMount: this.handleSearchPanelMount,
|
|
361
|
+
onDestroy: this.handleSearchPanelDestroy
|
|
362
|
+
}), _view.keymap.of(this.keymaps)];
|
|
337
363
|
}
|
|
338
364
|
get keymaps() {
|
|
339
365
|
// TODO: if more keymaps are added, list them in the docs as well (#Command keybinding)
|
|
@@ -536,14 +562,15 @@ let SourceCodeEditor = exports.SourceCodeEditor = (_dec = (0, _withDeterministic
|
|
|
536
562
|
const {
|
|
537
563
|
label,
|
|
538
564
|
styles,
|
|
565
|
+
searchConfig,
|
|
539
566
|
...restProps
|
|
540
567
|
} = this.props;
|
|
541
|
-
return (0, _jsxRuntime.
|
|
568
|
+
return (0, _jsxRuntime.jsxs)("div", {
|
|
542
569
|
"data-cid": "SourceCodeEditor",
|
|
543
570
|
ref: this.handleRef,
|
|
544
571
|
css: styles?.codeEditor,
|
|
545
572
|
...(0, _passthroughProps.passthroughProps)((0, _omitProps.omitProps)(restProps, SourceCodeEditor.allowedProps)),
|
|
546
|
-
children: (0, _jsxRuntime.jsxs)("label", {
|
|
573
|
+
children: [(0, _jsxRuntime.jsxs)("label", {
|
|
547
574
|
css: styles?.label,
|
|
548
575
|
id: this._id,
|
|
549
576
|
children: [(0, _jsxRuntime.jsx)(_ScreenReaderContent.ScreenReaderContent, {
|
|
@@ -552,7 +579,14 @@ let SourceCodeEditor = exports.SourceCodeEditor = (_dec = (0, _withDeterministic
|
|
|
552
579
|
ref: this.handleContainerRef,
|
|
553
580
|
css: styles?.codeEditorContainer
|
|
554
581
|
})]
|
|
555
|
-
})
|
|
582
|
+
}), searchConfig && this.state.searchPanels.map(({
|
|
583
|
+
id,
|
|
584
|
+
dom,
|
|
585
|
+
view
|
|
586
|
+
}) => /*#__PURE__*/(0, _reactDom.createPortal)((0, _jsxRuntime.jsx)(_SearchPanel.SearchPanel, {
|
|
587
|
+
view: view,
|
|
588
|
+
searchConfig: searchConfig
|
|
589
|
+
}), dom, String(id)))]
|
|
556
590
|
});
|
|
557
591
|
}
|
|
558
592
|
}) || _class) || _class) || _class);
|
|
@@ -150,6 +150,10 @@ const generateStyle = (componentTheme, props, sharedTokens) => {
|
|
|
150
150
|
'.cm-placeholder': {
|
|
151
151
|
// for better contrast
|
|
152
152
|
color: componentTheme.placeholderBackgroundColor
|
|
153
|
+
},
|
|
154
|
+
'.cm-panels': {
|
|
155
|
+
backgroundColor: componentTheme.background,
|
|
156
|
+
color: componentTheme.color
|
|
153
157
|
}
|
|
154
158
|
},
|
|
155
159
|
highlightStyle: [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@instructure/ui-source-code-editor",
|
|
3
|
-
"version": "11.7.4-snapshot-
|
|
3
|
+
"version": "11.7.4-snapshot-98",
|
|
4
4
|
"description": "A UI component library made by Instructure Inc.",
|
|
5
5
|
"author": "Instructure, Inc. Engineering and Product Design",
|
|
6
6
|
"module": "./es/index.js",
|
|
@@ -29,24 +29,24 @@
|
|
|
29
29
|
"@codemirror/state": "^6.4.1",
|
|
30
30
|
"@codemirror/view": "^6.34.1",
|
|
31
31
|
"@lezer/highlight": "1.2.3",
|
|
32
|
-
"@instructure/emotion": "11.7.4-snapshot-
|
|
33
|
-
"@instructure/
|
|
34
|
-
"@instructure/ui-buttons": "11.7.4-snapshot-
|
|
35
|
-
"@instructure/
|
|
36
|
-
"@instructure/ui-
|
|
37
|
-
"@instructure/ui-i18n": "11.7.4-snapshot-
|
|
38
|
-
"@instructure/ui-
|
|
39
|
-
"@instructure/ui-
|
|
40
|
-
"@instructure/ui-text-input": "11.7.4-snapshot-
|
|
41
|
-
"@instructure/ui-themes": "11.7.4-snapshot-
|
|
42
|
-
"@instructure/ui-utils": "11.7.4-snapshot-
|
|
32
|
+
"@instructure/emotion": "11.7.4-snapshot-98",
|
|
33
|
+
"@instructure/ui-a11y-content": "11.7.4-snapshot-98",
|
|
34
|
+
"@instructure/ui-buttons": "11.7.4-snapshot-98",
|
|
35
|
+
"@instructure/shared-types": "11.7.4-snapshot-98",
|
|
36
|
+
"@instructure/ui-dom-utils": "11.7.4-snapshot-98",
|
|
37
|
+
"@instructure/ui-i18n": "11.7.4-snapshot-98",
|
|
38
|
+
"@instructure/ui-icons": "11.7.4-snapshot-98",
|
|
39
|
+
"@instructure/ui-react-utils": "11.7.4-snapshot-98",
|
|
40
|
+
"@instructure/ui-text-input": "11.7.4-snapshot-98",
|
|
41
|
+
"@instructure/ui-themes": "11.7.4-snapshot-98",
|
|
42
|
+
"@instructure/ui-utils": "11.7.4-snapshot-98"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@testing-library/jest-dom": "^6.9.1",
|
|
46
46
|
"@testing-library/react": "16.3.2",
|
|
47
47
|
"@testing-library/user-event": "^14.6.1",
|
|
48
48
|
"vitest": "^4.1.9",
|
|
49
|
-
"@instructure/ui-babel-preset": "11.7.4-snapshot-
|
|
49
|
+
"@instructure/ui-babel-preset": "11.7.4-snapshot-98"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"react": ">=18 <=19",
|
|
@@ -39,7 +39,6 @@ import {
|
|
|
39
39
|
IconArrowOpenUpLine,
|
|
40
40
|
IconSearchLine
|
|
41
41
|
} from '@instructure/ui-icons'
|
|
42
|
-
import { createRoot } from 'react-dom/client'
|
|
43
42
|
|
|
44
43
|
export type SearchConfig = {
|
|
45
44
|
placeholder: string
|
|
@@ -47,7 +46,18 @@ export type SearchConfig = {
|
|
|
47
46
|
prevResultLabel: string
|
|
48
47
|
}
|
|
49
48
|
|
|
50
|
-
|
|
49
|
+
|
|
50
|
+
export type SearchPanelHandlers = {
|
|
51
|
+
onMount: (dom: HTMLElement, view: EditorView) => void
|
|
52
|
+
onDestroy: (dom: HTMLElement) => void
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* ---
|
|
57
|
+
* private: true
|
|
58
|
+
* ---
|
|
59
|
+
*/
|
|
60
|
+
export function SearchPanel({
|
|
51
61
|
view,
|
|
52
62
|
searchConfig
|
|
53
63
|
}: {
|
|
@@ -135,15 +145,24 @@ function SearchPanel({
|
|
|
135
145
|
)
|
|
136
146
|
}
|
|
137
147
|
|
|
138
|
-
export default function customSearch(
|
|
148
|
+
export default function customSearch(
|
|
149
|
+
searchConfig: SearchConfig | undefined,
|
|
150
|
+
handlers: SearchPanelHandlers
|
|
151
|
+
) {
|
|
139
152
|
return searchConfig
|
|
140
153
|
? search({
|
|
141
154
|
createPanel: (view) => {
|
|
142
155
|
const dom = document.createElement('div')
|
|
143
156
|
dom.style.padding = '8px'
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
157
|
+
return {
|
|
158
|
+
dom,
|
|
159
|
+
mount() {
|
|
160
|
+
handlers.onMount(dom, view)
|
|
161
|
+
},
|
|
162
|
+
destroy() {
|
|
163
|
+
handlers.onDestroy(dom)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
147
166
|
}
|
|
148
167
|
})
|
|
149
168
|
: []
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
import { Component } from 'react'
|
|
26
|
+
import { createPortal } from 'react-dom'
|
|
26
27
|
import { deepEqual as isEqual } from '@instructure/ui-utils'
|
|
27
28
|
|
|
28
29
|
import { EditorSelection, EditorState, StateEffect } from '@codemirror/state'
|
|
@@ -89,7 +90,7 @@ import { textDirectionContextConsumer } from '@instructure/ui-i18n'
|
|
|
89
90
|
|
|
90
91
|
import { withStyleNew } from '@instructure/emotion'
|
|
91
92
|
|
|
92
|
-
import customSearch from './SearchPanel.js'
|
|
93
|
+
import customSearch, { SearchPanel } from './SearchPanel.js'
|
|
93
94
|
|
|
94
95
|
import generateStyle from './styles.js'
|
|
95
96
|
|
|
@@ -98,6 +99,16 @@ import { rtlHorizontalArrowKeymap } from './customKeybinding.js'
|
|
|
98
99
|
import { allowedProps } from './props.js'
|
|
99
100
|
import type { SourceCodeEditorProps } from './props'
|
|
100
101
|
|
|
102
|
+
type SearchPanelInstance = {
|
|
103
|
+
id: number
|
|
104
|
+
dom: HTMLElement
|
|
105
|
+
view: EditorView
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
type SourceCodeEditorState = {
|
|
109
|
+
searchPanels: SearchPanelInstance[]
|
|
110
|
+
}
|
|
111
|
+
|
|
101
112
|
/**
|
|
102
113
|
---
|
|
103
114
|
category: components
|
|
@@ -106,7 +117,10 @@ category: components
|
|
|
106
117
|
@withDeterministicId()
|
|
107
118
|
@withStyleNew(generateStyle)
|
|
108
119
|
@textDirectionContextConsumer()
|
|
109
|
-
class SourceCodeEditor extends Component<
|
|
120
|
+
class SourceCodeEditor extends Component<
|
|
121
|
+
SourceCodeEditorProps,
|
|
122
|
+
SourceCodeEditorState
|
|
123
|
+
> {
|
|
110
124
|
static displayName = 'SourceCodeEditor'
|
|
111
125
|
static readonly componentId = 'SourceCodeEditor'
|
|
112
126
|
|
|
@@ -133,13 +147,35 @@ class SourceCodeEditor extends Component<SourceCodeEditorProps> {
|
|
|
133
147
|
|
|
134
148
|
ref: HTMLDivElement | null = null
|
|
135
149
|
|
|
150
|
+
state: SourceCodeEditorState = { searchPanels: [] }
|
|
151
|
+
|
|
136
152
|
private _containerRef?: HTMLDivElement
|
|
137
153
|
private _editorView?: EditorView
|
|
138
154
|
|
|
139
155
|
private _raf: RequestAnimationFrameType[] = []
|
|
140
156
|
|
|
157
|
+
private _searchPanelId = 0
|
|
158
|
+
private _isUnmounting = false
|
|
159
|
+
|
|
141
160
|
private _newSelectionAfterValueChange?: EditorSelection
|
|
142
161
|
|
|
162
|
+
handleSearchPanelMount = (dom: HTMLElement, view: EditorView) => {
|
|
163
|
+
this.setState((state) => ({
|
|
164
|
+
searchPanels: [
|
|
165
|
+
...state.searchPanels,
|
|
166
|
+
{ id: ++this._searchPanelId, dom, view }
|
|
167
|
+
]
|
|
168
|
+
}))
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
handleSearchPanelDestroy = (dom: HTMLElement) => {
|
|
172
|
+
if (this._isUnmounting) return
|
|
173
|
+
|
|
174
|
+
this.setState((state) => ({
|
|
175
|
+
searchPanels: state.searchPanels.filter((panel) => panel.dom !== dom)
|
|
176
|
+
}))
|
|
177
|
+
}
|
|
178
|
+
|
|
143
179
|
handleRef = (el: HTMLDivElement | null) => {
|
|
144
180
|
const { elementRef } = this.props
|
|
145
181
|
|
|
@@ -310,6 +346,7 @@ class SourceCodeEditor extends Component<SourceCodeEditorProps> {
|
|
|
310
346
|
}
|
|
311
347
|
|
|
312
348
|
componentWillUnmount() {
|
|
349
|
+
this._isUnmounting = true
|
|
313
350
|
this._editorView?.destroy()
|
|
314
351
|
|
|
315
352
|
this.cancelAnimationFrames()
|
|
@@ -349,7 +386,8 @@ class SourceCodeEditor extends Component<SourceCodeEditorProps> {
|
|
|
349
386
|
'indentWithTab',
|
|
350
387
|
'indentUnit',
|
|
351
388
|
'highlightActiveLine',
|
|
352
|
-
'attachment'
|
|
389
|
+
'attachment',
|
|
390
|
+
'searchConfig'
|
|
353
391
|
]
|
|
354
392
|
|
|
355
393
|
for (const prop of propsToObserve) {
|
|
@@ -438,7 +476,10 @@ class SourceCodeEditor extends Component<SourceCodeEditorProps> {
|
|
|
438
476
|
crosshairCursor(),
|
|
439
477
|
highlightSelectionMatches(),
|
|
440
478
|
indentOnInput(),
|
|
441
|
-
customSearch(this.props.searchConfig
|
|
479
|
+
customSearch(this.props.searchConfig, {
|
|
480
|
+
onMount: this.handleSearchPanelMount,
|
|
481
|
+
onDestroy: this.handleSearchPanelDestroy
|
|
482
|
+
}),
|
|
442
483
|
keymap.of(this.keymaps)
|
|
443
484
|
]
|
|
444
485
|
}
|
|
@@ -669,7 +710,7 @@ class SourceCodeEditor extends Component<SourceCodeEditorProps> {
|
|
|
669
710
|
}
|
|
670
711
|
|
|
671
712
|
render() {
|
|
672
|
-
const { label, styles, ...restProps } = this.props
|
|
713
|
+
const { label, styles, searchConfig, ...restProps } = this.props
|
|
673
714
|
|
|
674
715
|
return (
|
|
675
716
|
<div
|
|
@@ -687,6 +728,14 @@ class SourceCodeEditor extends Component<SourceCodeEditorProps> {
|
|
|
687
728
|
css={styles?.codeEditorContainer}
|
|
688
729
|
/>
|
|
689
730
|
</label>
|
|
731
|
+
{searchConfig &&
|
|
732
|
+
this.state.searchPanels.map(({ id, dom, view }) =>
|
|
733
|
+
createPortal(
|
|
734
|
+
<SearchPanel view={view} searchConfig={searchConfig} />,
|
|
735
|
+
dom,
|
|
736
|
+
String(id)
|
|
737
|
+
)
|
|
738
|
+
)}
|
|
690
739
|
</div>
|
|
691
740
|
)
|
|
692
741
|
}
|
|
@@ -160,6 +160,10 @@ const generateStyle = (
|
|
|
160
160
|
'.cm-placeholder': {
|
|
161
161
|
// for better contrast
|
|
162
162
|
color: componentTheme.placeholderBackgroundColor
|
|
163
|
+
},
|
|
164
|
+
'.cm-panels': {
|
|
165
|
+
backgroundColor: componentTheme.background,
|
|
166
|
+
color: componentTheme.color
|
|
163
167
|
}
|
|
164
168
|
},
|
|
165
169
|
|