@elice/material-exercise 1.221228.0 → 1.230111.0-mobexercise.1
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/cjs/components/material-exercise/MaterialExerciseMobile.d.ts +8 -0
- package/cjs/components/material-exercise/MaterialExerciseMobile.js +44 -0
- package/cjs/components/material-exercise/index.d.ts +2 -0
- package/cjs/components/shared/monaco-editor/MonacoEditor.d.ts +4 -1
- package/cjs/components/shared/monaco-editor/MonacoEditor.js +20 -2
- package/cjs/components/shared/monaco-editor/MonacoEditorLazy.js +8 -1
- package/cjs/components/shared/monaco-editor/MonacoEditorMobile.d.ts +3 -0
- package/cjs/components/shared/monaco-editor/MonacoEditorMobile.js +169 -0
- package/cjs/index.js +2 -0
- package/es/components/material-exercise/MaterialExerciseMobile.d.ts +8 -0
- package/es/components/material-exercise/MaterialExerciseMobile.js +38 -0
- package/es/components/material-exercise/index.d.ts +2 -0
- package/es/components/shared/monaco-editor/MonacoEditor.d.ts +4 -1
- package/es/components/shared/monaco-editor/MonacoEditor.js +20 -2
- package/es/components/shared/monaco-editor/MonacoEditorLazy.js +8 -1
- package/es/components/shared/monaco-editor/MonacoEditorMobile.d.ts +3 -0
- package/es/components/shared/monaco-editor/MonacoEditorMobile.js +144 -0
- package/es/index.js +1 -0
- package/package.json +4 -40
- package/cjs/components/material-exercise/exercise-file-editor/ExerciseMobileFileEditor.d.ts +0 -8
- package/es/components/material-exercise/exercise-file-editor/ExerciseMobileFileEditor.d.ts +0 -8
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { MaterialExerciseCommonProps } from './context';
|
|
3
|
+
declare type PickedMaterialExerciseCommonProps = Pick<MaterialExerciseCommonProps, 'materialExerciseId' | 'exerciseRoomId' | 'readOnlyEditor' | 'onExerciseRoomIdChange' | 'onDefaultExerciseRoomIdSet' | 'onError'>;
|
|
4
|
+
export interface MaterialExerciseMobileProps extends PickedMaterialExerciseCommonProps {
|
|
5
|
+
filename: string;
|
|
6
|
+
}
|
|
7
|
+
declare const _default: React.VFC<MaterialExerciseMobileProps & import("@elice/material-shared-utils").MaterialCommponentCommonProps>;
|
|
8
|
+
export default _default;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React = require('react');
|
|
4
|
+
var apiClient = require('@elice/api-client');
|
|
5
|
+
var materialSharedUtils = require('@elice/material-shared-utils');
|
|
6
|
+
var recoil = require('recoil');
|
|
7
|
+
var recoil$1 = require('./context/recoil.js');
|
|
8
|
+
require('./context/context.js');
|
|
9
|
+
require('./context/recoilTypes.js');
|
|
10
|
+
require('./context/subjects.js');
|
|
11
|
+
var ExerciseProvider = require('./context/ExerciseProvider.js');
|
|
12
|
+
var ExerciseFile = require('./exercise-file/ExerciseFile.js');
|
|
13
|
+
|
|
14
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
15
|
+
|
|
16
|
+
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
|
|
17
|
+
|
|
18
|
+
//
|
|
19
|
+
//
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Material exercise.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const MaterialExerciseMobile = ({
|
|
26
|
+
filename
|
|
27
|
+
}) => {
|
|
28
|
+
const setExerciseActiveFilename = recoil.useSetRecoilState(recoil$1.exerciseActiveFilenameState);
|
|
29
|
+
React__default["default"].useEffect(() => setExerciseActiveFilename(filename), // eslint-disable-next-line react-hooks/exhaustive-deps
|
|
30
|
+
[filename]); //
|
|
31
|
+
//
|
|
32
|
+
//
|
|
33
|
+
|
|
34
|
+
return React__default["default"].createElement(ExerciseFile, null);
|
|
35
|
+
}; //
|
|
36
|
+
//
|
|
37
|
+
//
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
var MaterialExerciseMobile$1 = materialSharedUtils.withMaterial(props => React__default["default"].createElement(ExerciseProvider, Object.assign({}, props), React__default["default"].createElement(MaterialExerciseMobile, Object.assign({}, props))), apiClient.config.init, {
|
|
41
|
+
overrideRecoilScope: true
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
module.exports = MaterialExerciseMobile$1;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { default as MaterialExercise } from './MaterialExercise';
|
|
2
2
|
export type { MaterialExerciseProps, MaterialExerciseApis, } from './MaterialExercise';
|
|
3
|
+
export { default as MaterialExerciseMobile } from './MaterialExerciseMobile';
|
|
4
|
+
export type { MaterialExerciseMobileProps } from './MaterialExerciseMobile';
|
|
3
5
|
export type { MaterialExerciseCommonApis, MaterialExerciseCommonApiSendTextToTerminal, MaterialExerciseCommonProps, MaterialExerciseCommonPropExerciseImageEmptyOptions, MaterialExerciseCommonPropOnCodeHelpRequest, MaterialExerciseCommonPropOnDefaultExerciseRoomIdSet, MaterialExerciseCommonPropOnExerciseRoomIdChange, MaterialExerciseCommonPropOnRunningDone, MaterialExerciseCommonPropOnSubmit, MaterialExerciseCommonPropOnError, } from './context/types';
|
|
@@ -15,9 +15,11 @@ declare type MonacoEditorApiGetValueInRange = (from: number, to: number) => stri
|
|
|
15
15
|
/** Set (overwrite) value to editor. */
|
|
16
16
|
declare type MonacoEditorApiSetValue = (value: string) => void;
|
|
17
17
|
/** Insert value at specific position. */
|
|
18
|
-
declare type MonacoEditorApiInsertValue = (
|
|
18
|
+
declare type MonacoEditorApiInsertValue = (offset: number, value: string) => void;
|
|
19
19
|
/** Remove value at specific range. */
|
|
20
20
|
declare type MonacoEditorApiRemoveValue = (from: number, to: number) => void;
|
|
21
|
+
/** Get offset of current cursor position. */
|
|
22
|
+
declare type MonacoEditorApiGetPositionOffset = () => number;
|
|
21
23
|
/** Set selection of editor with specific range. */
|
|
22
24
|
declare type MonacoEditorApiSetSelection = (from: number, to: number) => void;
|
|
23
25
|
/** Scroll editor to specific `scrollTop`. */
|
|
@@ -33,6 +35,7 @@ export interface MonacoEditorApis {
|
|
|
33
35
|
setValue: MonacoEditorApiSetValue;
|
|
34
36
|
insertValue: MonacoEditorApiInsertValue;
|
|
35
37
|
removeValue: MonacoEditorApiRemoveValue;
|
|
38
|
+
getPositionOffset: MonacoEditorApiGetPositionOffset;
|
|
36
39
|
setSelection: MonacoEditorApiSetSelection;
|
|
37
40
|
scrollTo: MonacoEditorApiScrollTo;
|
|
38
41
|
}
|
|
@@ -210,7 +210,7 @@ const MonacoEditor = React.forwardRef(({
|
|
|
210
210
|
*/
|
|
211
211
|
|
|
212
212
|
|
|
213
|
-
const insertValue = (
|
|
213
|
+
const insertValue = (offset, newValue) => {
|
|
214
214
|
if (!editor.current) {
|
|
215
215
|
return;
|
|
216
216
|
} // Replace value EOL as LF.
|
|
@@ -220,7 +220,7 @@ const MonacoEditor = React.forwardRef(({
|
|
|
220
220
|
const model = getMonacoModel();
|
|
221
221
|
|
|
222
222
|
if (model) {
|
|
223
|
-
const position = model.getPositionAt(
|
|
223
|
+
const position = model.getPositionAt(offset);
|
|
224
224
|
suppress.current = true;
|
|
225
225
|
editor.current.pushUndoStop();
|
|
226
226
|
model.pushEditOperations([], [{
|
|
@@ -259,6 +259,23 @@ const MonacoEditor = React.forwardRef(({
|
|
|
259
259
|
suppress.current = false;
|
|
260
260
|
}
|
|
261
261
|
};
|
|
262
|
+
/**
|
|
263
|
+
* Get offset of current cursor position.
|
|
264
|
+
* @public
|
|
265
|
+
*/
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
const getPositionOffset = () => {
|
|
269
|
+
var _a;
|
|
270
|
+
|
|
271
|
+
const model = getMonacoModel();
|
|
272
|
+
|
|
273
|
+
if (!editor.current || !model) {
|
|
274
|
+
return 0;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return model.getOffsetAt((_a = editor.current.getPosition()) !== null && _a !== void 0 ? _a : new monaco__namespace.Position(0, 0));
|
|
278
|
+
};
|
|
262
279
|
/**
|
|
263
280
|
* Set selection of editor with specific range.
|
|
264
281
|
* @public
|
|
@@ -363,6 +380,7 @@ const MonacoEditor = React.forwardRef(({
|
|
|
363
380
|
editorApis.current.setValue = setValue;
|
|
364
381
|
editorApis.current.insertValue = insertValue;
|
|
365
382
|
editorApis.current.removeValue = removeValue;
|
|
383
|
+
editorApis.current.getPositionOffset = getPositionOffset;
|
|
366
384
|
editorApis.current.setSelection = setSelection;
|
|
367
385
|
editorApis.current.scrollTo = scrollTo;
|
|
368
386
|
React__default["default"].useImperativeHandle(ref, () => editorApis.current, [editorApis]); //
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var React = require('react');
|
|
4
|
+
var utils = require('@elice/utils');
|
|
4
5
|
var ExerciseFileShimmer = require('../exercise-shimmer/ExerciseFileShimmer.js');
|
|
5
6
|
require('../exercise-shimmer/ExerciseFileTabsShimmer.js');
|
|
6
7
|
require('../exercise-shimmer/ExerciseFileTabShimmer.js');
|
|
@@ -13,7 +14,13 @@ function _interopNamespaceDefaultOnly (e) { return Object.freeze({ __proto__: nu
|
|
|
13
14
|
|
|
14
15
|
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
//
|
|
18
|
+
//
|
|
19
|
+
|
|
20
|
+
const AsyncMonacoEditor = React__default["default"].lazy(() => utils.FlutterApp.checkWebview() ? Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespaceDefaultOnly(require('./MonacoEditorMobile.js')); }) : Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespaceDefaultOnly(require('./MonacoEditor.js')); })); //
|
|
21
|
+
//
|
|
22
|
+
//
|
|
23
|
+
|
|
17
24
|
const MonacoEditorLazy = React.forwardRef((props, ref) => {
|
|
18
25
|
return React__default["default"].createElement(React__default["default"].Suspense, {
|
|
19
26
|
fallback: React__default["default"].createElement(ExerciseFileShimmer, null)
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React = require('react');
|
|
4
|
+
var reactUse = require('react-use');
|
|
5
|
+
var utils = require('@elice/utils');
|
|
6
|
+
var monaco = require('monaco-editor/esm/vs/editor/editor.api');
|
|
7
|
+
var MonacoEditor = require('./MonacoEditor.js');
|
|
8
|
+
|
|
9
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
10
|
+
|
|
11
|
+
function _interopNamespace(e) {
|
|
12
|
+
if (e && e.__esModule) return e;
|
|
13
|
+
var n = Object.create(null);
|
|
14
|
+
if (e) {
|
|
15
|
+
Object.keys(e).forEach(function (k) {
|
|
16
|
+
if (k !== 'default') {
|
|
17
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
18
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () { return e[k]; }
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
n["default"] = e;
|
|
26
|
+
return Object.freeze(n);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
|
|
30
|
+
var monaco__namespace = /*#__PURE__*/_interopNamespace(monaco);
|
|
31
|
+
|
|
32
|
+
//
|
|
33
|
+
//
|
|
34
|
+
|
|
35
|
+
const MOBILE_EDITOR_PREFERENCE = Object.freeze({
|
|
36
|
+
autoClosingBrackets: 'always',
|
|
37
|
+
cursorSmoothCaretAnimation: false,
|
|
38
|
+
detectIndentation: true,
|
|
39
|
+
fontSize: 16,
|
|
40
|
+
minimap: {
|
|
41
|
+
enabled: false
|
|
42
|
+
},
|
|
43
|
+
renderWhitespace: 'all',
|
|
44
|
+
tabSize: 4,
|
|
45
|
+
theme: 'elice',
|
|
46
|
+
wordWrap: 'on'
|
|
47
|
+
});
|
|
48
|
+
const IS_IOS = ['iPad Simulator', 'iPhone Simulator', 'iPod Simulator', 'iPad', 'iPhone', 'iPod'].includes(navigator.platform) || navigator.userAgent.includes('Mac') && 'ontouchend' in document; //
|
|
49
|
+
//
|
|
50
|
+
//
|
|
51
|
+
|
|
52
|
+
const MonacoEditorMobile = React.forwardRef((props, ref) => {
|
|
53
|
+
const editorApiRef = reactUse.useEnsuredForwardedRef(ref);
|
|
54
|
+
const [preference, setPreference] = React__default["default"].useState(MOBILE_EDITOR_PREFERENCE);
|
|
55
|
+
/**
|
|
56
|
+
* Add a button to show keyboard on non-Apple devices.
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
const addKeyboardButtonOverlayWidget = editor => {
|
|
60
|
+
if (IS_IOS) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
editor.addOverlayWidget({
|
|
65
|
+
getId: () => {
|
|
66
|
+
return 'editor.contrib.ShowKeyboardWidget';
|
|
67
|
+
},
|
|
68
|
+
getDomNode: () => {
|
|
69
|
+
const domNode = document.createElement('textarea');
|
|
70
|
+
domNode.className = 'iPadShowKeyboard';
|
|
71
|
+
domNode.addEventListener('touchstart', () => editor.focus());
|
|
72
|
+
domNode.addEventListener('focus', () => editor.focus());
|
|
73
|
+
return domNode;
|
|
74
|
+
},
|
|
75
|
+
getPosition: () => {
|
|
76
|
+
return {
|
|
77
|
+
preference: monaco__namespace.editor.OverlayWidgetPositionPreference.BOTTOM_RIGHT_CORNER
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Export window-level event functions for mobile app.
|
|
84
|
+
*/
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
const exposeWindowGlobalEvents = editor => {
|
|
88
|
+
const editorApi = editorApiRef.current;
|
|
89
|
+
|
|
90
|
+
if (!editorApi) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
window.cursorCharLeft = () => {
|
|
95
|
+
var _a;
|
|
96
|
+
|
|
97
|
+
const pos = (_a = editor.getPosition()) !== null && _a !== void 0 ? _a : new monaco__namespace.Position(0, 0);
|
|
98
|
+
editor.setPosition(new monaco__namespace.Position(pos.lineNumber, pos.column - 1));
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
window.cursorCharRight = () => {
|
|
102
|
+
var _a;
|
|
103
|
+
|
|
104
|
+
const pos = (_a = editor.getPosition()) !== null && _a !== void 0 ? _a : new monaco__namespace.Position(0, 0);
|
|
105
|
+
editor.setPosition(new monaco__namespace.Position(pos.lineNumber, pos.column + 1));
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
window.cursorLineUp = () => {
|
|
109
|
+
var _a;
|
|
110
|
+
|
|
111
|
+
const pos = (_a = editor.getPosition()) !== null && _a !== void 0 ? _a : new monaco__namespace.Position(0, 0);
|
|
112
|
+
editor.setPosition(new monaco__namespace.Position(pos.lineNumber - 1, pos.column));
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
window.cursorLineDown = () => {
|
|
116
|
+
var _a;
|
|
117
|
+
|
|
118
|
+
const pos = (_a = editor.getPosition()) !== null && _a !== void 0 ? _a : new monaco__namespace.Position(0, 0);
|
|
119
|
+
editor.setPosition(new monaco__namespace.Position(pos.lineNumber + 1, pos.column));
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
window.dispatchReconfigureFontSize = fontSize => {
|
|
123
|
+
setPreference(prev => Object.assign(Object.assign({}, prev), {
|
|
124
|
+
fontSize
|
|
125
|
+
}));
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
window.dispatchChangeInsert = insert => {
|
|
129
|
+
editorApi.insertValue(editorApi.getPositionOffset(), insert);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
window.insertTab = () => {
|
|
133
|
+
const model = editorApi.getMonacoModel();
|
|
134
|
+
|
|
135
|
+
if (model) {
|
|
136
|
+
const modelOptions = model.getOptions();
|
|
137
|
+
editorApi.insertValue(editorApi.getPositionOffset(), modelOptions.insertSpaces ? ' '.repeat(modelOptions.tabSize) : '\t');
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
};
|
|
141
|
+
/**
|
|
142
|
+
*
|
|
143
|
+
*/
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
const handleLoad = editor => {
|
|
147
|
+
if (typeof props.onLoad === 'function') {
|
|
148
|
+
props.onLoad(editor);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
addKeyboardButtonOverlayWidget(editor);
|
|
152
|
+
exposeWindowGlobalEvents(editor);
|
|
153
|
+
editor.onDidChangeModelContent(() => {
|
|
154
|
+
utils.FlutterApp.onDidChangeContent();
|
|
155
|
+
});
|
|
156
|
+
}; //
|
|
157
|
+
//
|
|
158
|
+
//
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
return React__default["default"].createElement(MonacoEditor, Object.assign({}, props, {
|
|
162
|
+
noLanguageIntellisense: true,
|
|
163
|
+
preference: preference,
|
|
164
|
+
onLoad: handleLoad,
|
|
165
|
+
ref: editorApiRef
|
|
166
|
+
}));
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
module.exports = MonacoEditorMobile;
|
package/cjs/index.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var MaterialExercise = require('./components/material-exercise/MaterialExercise.js');
|
|
6
|
+
var MaterialExerciseMobile = require('./components/material-exercise/MaterialExerciseMobile.js');
|
|
6
7
|
var MonacoEditorLazy = require('./components/shared/monaco-editor/MonacoEditorLazy.js');
|
|
7
8
|
var MonacoEditorPerferenceForm = require('./components/shared/monaco-editor/MonacoEditorPerferenceForm.js');
|
|
8
9
|
var monacoLanguage = require('./components/shared/monaco-editor/utils/monacoLanguage.js');
|
|
@@ -13,6 +14,7 @@ var preferences = require('./components/shared/monaco-editor/constants/monaco/pr
|
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
exports.MaterialExercise = MaterialExercise;
|
|
17
|
+
exports.MaterialExerciseMobile = MaterialExerciseMobile;
|
|
16
18
|
exports.MonacoEditorLazy = MonacoEditorLazy;
|
|
17
19
|
exports.MonacoEditorPerference = MonacoEditorPerferenceForm;
|
|
18
20
|
exports.getLanguageFromFilename = monacoLanguage.getLanguageFromFilename;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { MaterialExerciseCommonProps } from './context';
|
|
3
|
+
declare type PickedMaterialExerciseCommonProps = Pick<MaterialExerciseCommonProps, 'materialExerciseId' | 'exerciseRoomId' | 'readOnlyEditor' | 'onExerciseRoomIdChange' | 'onDefaultExerciseRoomIdSet' | 'onError'>;
|
|
4
|
+
export interface MaterialExerciseMobileProps extends PickedMaterialExerciseCommonProps {
|
|
5
|
+
filename: string;
|
|
6
|
+
}
|
|
7
|
+
declare const _default: React.VFC<MaterialExerciseMobileProps & import("@elice/material-shared-utils").MaterialCommponentCommonProps>;
|
|
8
|
+
export default _default;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { config } from '@elice/api-client';
|
|
3
|
+
import { withMaterial } from '@elice/material-shared-utils';
|
|
4
|
+
import { useSetRecoilState } from 'recoil';
|
|
5
|
+
import { exerciseActiveFilenameState } from './context/recoil.js';
|
|
6
|
+
import './context/context.js';
|
|
7
|
+
import './context/recoilTypes.js';
|
|
8
|
+
import './context/subjects.js';
|
|
9
|
+
import ExerciseProvider from './context/ExerciseProvider.js';
|
|
10
|
+
import ExerciseFile from './exercise-file/ExerciseFile.js';
|
|
11
|
+
|
|
12
|
+
//
|
|
13
|
+
//
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Material exercise.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const MaterialExerciseMobile = ({
|
|
20
|
+
filename
|
|
21
|
+
}) => {
|
|
22
|
+
const setExerciseActiveFilename = useSetRecoilState(exerciseActiveFilenameState);
|
|
23
|
+
React.useEffect(() => setExerciseActiveFilename(filename), // eslint-disable-next-line react-hooks/exhaustive-deps
|
|
24
|
+
[filename]); //
|
|
25
|
+
//
|
|
26
|
+
//
|
|
27
|
+
|
|
28
|
+
return React.createElement(ExerciseFile, null);
|
|
29
|
+
}; //
|
|
30
|
+
//
|
|
31
|
+
//
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
var MaterialExerciseMobile$1 = withMaterial(props => React.createElement(ExerciseProvider, Object.assign({}, props), React.createElement(MaterialExerciseMobile, Object.assign({}, props))), config.init, {
|
|
35
|
+
overrideRecoilScope: true
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
export { MaterialExerciseMobile$1 as default };
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { default as MaterialExercise } from './MaterialExercise';
|
|
2
2
|
export type { MaterialExerciseProps, MaterialExerciseApis, } from './MaterialExercise';
|
|
3
|
+
export { default as MaterialExerciseMobile } from './MaterialExerciseMobile';
|
|
4
|
+
export type { MaterialExerciseMobileProps } from './MaterialExerciseMobile';
|
|
3
5
|
export type { MaterialExerciseCommonApis, MaterialExerciseCommonApiSendTextToTerminal, MaterialExerciseCommonProps, MaterialExerciseCommonPropExerciseImageEmptyOptions, MaterialExerciseCommonPropOnCodeHelpRequest, MaterialExerciseCommonPropOnDefaultExerciseRoomIdSet, MaterialExerciseCommonPropOnExerciseRoomIdChange, MaterialExerciseCommonPropOnRunningDone, MaterialExerciseCommonPropOnSubmit, MaterialExerciseCommonPropOnError, } from './context/types';
|
|
@@ -15,9 +15,11 @@ declare type MonacoEditorApiGetValueInRange = (from: number, to: number) => stri
|
|
|
15
15
|
/** Set (overwrite) value to editor. */
|
|
16
16
|
declare type MonacoEditorApiSetValue = (value: string) => void;
|
|
17
17
|
/** Insert value at specific position. */
|
|
18
|
-
declare type MonacoEditorApiInsertValue = (
|
|
18
|
+
declare type MonacoEditorApiInsertValue = (offset: number, value: string) => void;
|
|
19
19
|
/** Remove value at specific range. */
|
|
20
20
|
declare type MonacoEditorApiRemoveValue = (from: number, to: number) => void;
|
|
21
|
+
/** Get offset of current cursor position. */
|
|
22
|
+
declare type MonacoEditorApiGetPositionOffset = () => number;
|
|
21
23
|
/** Set selection of editor with specific range. */
|
|
22
24
|
declare type MonacoEditorApiSetSelection = (from: number, to: number) => void;
|
|
23
25
|
/** Scroll editor to specific `scrollTop`. */
|
|
@@ -33,6 +35,7 @@ export interface MonacoEditorApis {
|
|
|
33
35
|
setValue: MonacoEditorApiSetValue;
|
|
34
36
|
insertValue: MonacoEditorApiInsertValue;
|
|
35
37
|
removeValue: MonacoEditorApiRemoveValue;
|
|
38
|
+
getPositionOffset: MonacoEditorApiGetPositionOffset;
|
|
36
39
|
setSelection: MonacoEditorApiSetSelection;
|
|
37
40
|
scrollTo: MonacoEditorApiScrollTo;
|
|
38
41
|
}
|
|
@@ -184,7 +184,7 @@ const MonacoEditor = forwardRef(({
|
|
|
184
184
|
*/
|
|
185
185
|
|
|
186
186
|
|
|
187
|
-
const insertValue = (
|
|
187
|
+
const insertValue = (offset, newValue) => {
|
|
188
188
|
if (!editor.current) {
|
|
189
189
|
return;
|
|
190
190
|
} // Replace value EOL as LF.
|
|
@@ -194,7 +194,7 @@ const MonacoEditor = forwardRef(({
|
|
|
194
194
|
const model = getMonacoModel();
|
|
195
195
|
|
|
196
196
|
if (model) {
|
|
197
|
-
const position = model.getPositionAt(
|
|
197
|
+
const position = model.getPositionAt(offset);
|
|
198
198
|
suppress.current = true;
|
|
199
199
|
editor.current.pushUndoStop();
|
|
200
200
|
model.pushEditOperations([], [{
|
|
@@ -233,6 +233,23 @@ const MonacoEditor = forwardRef(({
|
|
|
233
233
|
suppress.current = false;
|
|
234
234
|
}
|
|
235
235
|
};
|
|
236
|
+
/**
|
|
237
|
+
* Get offset of current cursor position.
|
|
238
|
+
* @public
|
|
239
|
+
*/
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
const getPositionOffset = () => {
|
|
243
|
+
var _a;
|
|
244
|
+
|
|
245
|
+
const model = getMonacoModel();
|
|
246
|
+
|
|
247
|
+
if (!editor.current || !model) {
|
|
248
|
+
return 0;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return model.getOffsetAt((_a = editor.current.getPosition()) !== null && _a !== void 0 ? _a : new monaco.Position(0, 0));
|
|
252
|
+
};
|
|
236
253
|
/**
|
|
237
254
|
* Set selection of editor with specific range.
|
|
238
255
|
* @public
|
|
@@ -337,6 +354,7 @@ const MonacoEditor = forwardRef(({
|
|
|
337
354
|
editorApis.current.setValue = setValue;
|
|
338
355
|
editorApis.current.insertValue = insertValue;
|
|
339
356
|
editorApis.current.removeValue = removeValue;
|
|
357
|
+
editorApis.current.getPositionOffset = getPositionOffset;
|
|
340
358
|
editorApis.current.setSelection = setSelection;
|
|
341
359
|
editorApis.current.scrollTo = scrollTo;
|
|
342
360
|
React.useImperativeHandle(ref, () => editorApis.current, [editorApis]); //
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import React, { forwardRef } from 'react';
|
|
2
|
+
import { FlutterApp } from '@elice/utils';
|
|
2
3
|
import ExerciseFileShimmer from '../exercise-shimmer/ExerciseFileShimmer.js';
|
|
3
4
|
import '../exercise-shimmer/ExerciseFileTabsShimmer.js';
|
|
4
5
|
import '../exercise-shimmer/ExerciseFileTabShimmer.js';
|
|
5
6
|
import '../exercise-shimmer/ExerciseFileTreeListShimmer.js';
|
|
6
7
|
import '../exercise-shimmer/ExerciseFileTreeListItemShimmer.js';
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
//
|
|
10
|
+
//
|
|
11
|
+
|
|
12
|
+
const AsyncMonacoEditor = React.lazy(() => FlutterApp.checkWebview() ? import('./MonacoEditorMobile.js') : import('./MonacoEditor.js')); //
|
|
13
|
+
//
|
|
14
|
+
//
|
|
15
|
+
|
|
9
16
|
const MonacoEditorLazy = forwardRef((props, ref) => {
|
|
10
17
|
return React.createElement(React.Suspense, {
|
|
11
18
|
fallback: React.createElement(ExerciseFileShimmer, null)
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react';
|
|
2
|
+
import { useEnsuredForwardedRef } from 'react-use';
|
|
3
|
+
import { FlutterApp } from '@elice/utils';
|
|
4
|
+
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
|
|
5
|
+
import MonacoEditor from './MonacoEditor.js';
|
|
6
|
+
|
|
7
|
+
//
|
|
8
|
+
//
|
|
9
|
+
|
|
10
|
+
const MOBILE_EDITOR_PREFERENCE = Object.freeze({
|
|
11
|
+
autoClosingBrackets: 'always',
|
|
12
|
+
cursorSmoothCaretAnimation: false,
|
|
13
|
+
detectIndentation: true,
|
|
14
|
+
fontSize: 16,
|
|
15
|
+
minimap: {
|
|
16
|
+
enabled: false
|
|
17
|
+
},
|
|
18
|
+
renderWhitespace: 'all',
|
|
19
|
+
tabSize: 4,
|
|
20
|
+
theme: 'elice',
|
|
21
|
+
wordWrap: 'on'
|
|
22
|
+
});
|
|
23
|
+
const IS_IOS = ['iPad Simulator', 'iPhone Simulator', 'iPod Simulator', 'iPad', 'iPhone', 'iPod'].includes(navigator.platform) || navigator.userAgent.includes('Mac') && 'ontouchend' in document; //
|
|
24
|
+
//
|
|
25
|
+
//
|
|
26
|
+
|
|
27
|
+
const MonacoEditorMobile = forwardRef((props, ref) => {
|
|
28
|
+
const editorApiRef = useEnsuredForwardedRef(ref);
|
|
29
|
+
const [preference, setPreference] = React.useState(MOBILE_EDITOR_PREFERENCE);
|
|
30
|
+
/**
|
|
31
|
+
* Add a button to show keyboard on non-Apple devices.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
const addKeyboardButtonOverlayWidget = editor => {
|
|
35
|
+
if (IS_IOS) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
editor.addOverlayWidget({
|
|
40
|
+
getId: () => {
|
|
41
|
+
return 'editor.contrib.ShowKeyboardWidget';
|
|
42
|
+
},
|
|
43
|
+
getDomNode: () => {
|
|
44
|
+
const domNode = document.createElement('textarea');
|
|
45
|
+
domNode.className = 'iPadShowKeyboard';
|
|
46
|
+
domNode.addEventListener('touchstart', () => editor.focus());
|
|
47
|
+
domNode.addEventListener('focus', () => editor.focus());
|
|
48
|
+
return domNode;
|
|
49
|
+
},
|
|
50
|
+
getPosition: () => {
|
|
51
|
+
return {
|
|
52
|
+
preference: monaco.editor.OverlayWidgetPositionPreference.BOTTOM_RIGHT_CORNER
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Export window-level event functions for mobile app.
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
const exposeWindowGlobalEvents = editor => {
|
|
63
|
+
const editorApi = editorApiRef.current;
|
|
64
|
+
|
|
65
|
+
if (!editorApi) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
window.cursorCharLeft = () => {
|
|
70
|
+
var _a;
|
|
71
|
+
|
|
72
|
+
const pos = (_a = editor.getPosition()) !== null && _a !== void 0 ? _a : new monaco.Position(0, 0);
|
|
73
|
+
editor.setPosition(new monaco.Position(pos.lineNumber, pos.column - 1));
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
window.cursorCharRight = () => {
|
|
77
|
+
var _a;
|
|
78
|
+
|
|
79
|
+
const pos = (_a = editor.getPosition()) !== null && _a !== void 0 ? _a : new monaco.Position(0, 0);
|
|
80
|
+
editor.setPosition(new monaco.Position(pos.lineNumber, pos.column + 1));
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
window.cursorLineUp = () => {
|
|
84
|
+
var _a;
|
|
85
|
+
|
|
86
|
+
const pos = (_a = editor.getPosition()) !== null && _a !== void 0 ? _a : new monaco.Position(0, 0);
|
|
87
|
+
editor.setPosition(new monaco.Position(pos.lineNumber - 1, pos.column));
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
window.cursorLineDown = () => {
|
|
91
|
+
var _a;
|
|
92
|
+
|
|
93
|
+
const pos = (_a = editor.getPosition()) !== null && _a !== void 0 ? _a : new monaco.Position(0, 0);
|
|
94
|
+
editor.setPosition(new monaco.Position(pos.lineNumber + 1, pos.column));
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
window.dispatchReconfigureFontSize = fontSize => {
|
|
98
|
+
setPreference(prev => Object.assign(Object.assign({}, prev), {
|
|
99
|
+
fontSize
|
|
100
|
+
}));
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
window.dispatchChangeInsert = insert => {
|
|
104
|
+
editorApi.insertValue(editorApi.getPositionOffset(), insert);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
window.insertTab = () => {
|
|
108
|
+
const model = editorApi.getMonacoModel();
|
|
109
|
+
|
|
110
|
+
if (model) {
|
|
111
|
+
const modelOptions = model.getOptions();
|
|
112
|
+
editorApi.insertValue(editorApi.getPositionOffset(), modelOptions.insertSpaces ? ' '.repeat(modelOptions.tabSize) : '\t');
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
*
|
|
118
|
+
*/
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
const handleLoad = editor => {
|
|
122
|
+
if (typeof props.onLoad === 'function') {
|
|
123
|
+
props.onLoad(editor);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
addKeyboardButtonOverlayWidget(editor);
|
|
127
|
+
exposeWindowGlobalEvents(editor);
|
|
128
|
+
editor.onDidChangeModelContent(() => {
|
|
129
|
+
FlutterApp.onDidChangeContent();
|
|
130
|
+
});
|
|
131
|
+
}; //
|
|
132
|
+
//
|
|
133
|
+
//
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
return React.createElement(MonacoEditor, Object.assign({}, props, {
|
|
137
|
+
noLanguageIntellisense: true,
|
|
138
|
+
preference: preference,
|
|
139
|
+
onLoad: handleLoad,
|
|
140
|
+
ref: editorApiRef
|
|
141
|
+
}));
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
export { MonacoEditorMobile as default };
|
package/es/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { default as MaterialExercise } from './components/material-exercise/MaterialExercise.js';
|
|
2
|
+
export { default as MaterialExerciseMobile } from './components/material-exercise/MaterialExerciseMobile.js';
|
|
2
3
|
export { default as MonacoEditorLazy } from './components/shared/monaco-editor/MonacoEditorLazy.js';
|
|
3
4
|
export { default as MonacoEditorPerference } from './components/shared/monaco-editor/MonacoEditorPerferenceForm.js';
|
|
4
5
|
export { getLanguageFromFilename } from './components/shared/monaco-editor/utils/monacoLanguage.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elice/material-exercise",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.230111.0-mobexercise.1",
|
|
4
4
|
"description": "User view and editing components of Elice material exercise",
|
|
5
5
|
"repository": "https://git.elicer.io/elice/frontend/library/elice-material",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -46,25 +46,6 @@
|
|
|
46
46
|
"recoil": "^0.6.1",
|
|
47
47
|
"styled-components": "^5.2.0"
|
|
48
48
|
},
|
|
49
|
-
"optionalDependencies": {
|
|
50
|
-
"@codemirror/basic-setup": "^0.19.1",
|
|
51
|
-
"@codemirror/commands": "^0.19.8",
|
|
52
|
-
"@codemirror/highlight": "^0.19.7",
|
|
53
|
-
"@codemirror/lang-cpp": "^0.19.1",
|
|
54
|
-
"@codemirror/lang-css": "^0.19.3",
|
|
55
|
-
"@codemirror/lang-html": "^0.19.4",
|
|
56
|
-
"@codemirror/lang-java": "^0.19.1",
|
|
57
|
-
"@codemirror/lang-javascript": "^0.19.7",
|
|
58
|
-
"@codemirror/lang-json": "^0.19.2",
|
|
59
|
-
"@codemirror/lang-markdown": "^0.19.6",
|
|
60
|
-
"@codemirror/lang-python": "^0.19.4",
|
|
61
|
-
"@codemirror/lang-rust": "^0.19.2",
|
|
62
|
-
"@codemirror/lang-sql": "^0.19.4",
|
|
63
|
-
"@codemirror/lang-xml": "^0.19.2",
|
|
64
|
-
"@codemirror/state": "^0.19.9",
|
|
65
|
-
"@codemirror/text": "^0.19.6",
|
|
66
|
-
"@codemirror/view": "^0.19.47"
|
|
67
|
-
},
|
|
68
49
|
"dependencies": {
|
|
69
50
|
"@novnc/novnc": "^1.3.0",
|
|
70
51
|
"@nteract/notebook-preview": "^11.0.4-alpha.0",
|
|
@@ -93,30 +74,13 @@
|
|
|
93
74
|
"xterm-addon-fit": "^0.5.0"
|
|
94
75
|
},
|
|
95
76
|
"devDependencies": {
|
|
96
|
-
"@codemirror/basic-setup": "^0.19.1",
|
|
97
|
-
"@codemirror/commands": "^0.19.8",
|
|
98
|
-
"@codemirror/highlight": "^0.19.7",
|
|
99
|
-
"@codemirror/lang-cpp": "^0.19.1",
|
|
100
|
-
"@codemirror/lang-css": "^0.19.3",
|
|
101
|
-
"@codemirror/lang-html": "^0.19.4",
|
|
102
|
-
"@codemirror/lang-java": "^0.19.1",
|
|
103
|
-
"@codemirror/lang-javascript": "^0.19.7",
|
|
104
|
-
"@codemirror/lang-json": "^0.19.2",
|
|
105
|
-
"@codemirror/lang-markdown": "^0.19.6",
|
|
106
|
-
"@codemirror/lang-python": "^0.19.4",
|
|
107
|
-
"@codemirror/lang-rust": "^0.19.2",
|
|
108
|
-
"@codemirror/lang-sql": "^0.19.4",
|
|
109
|
-
"@codemirror/lang-xml": "^0.19.2",
|
|
110
|
-
"@codemirror/state": "^0.19.9",
|
|
111
|
-
"@codemirror/text": "^0.19.6",
|
|
112
|
-
"@codemirror/view": "^0.19.47",
|
|
113
77
|
"@elice/api-client": "1.221217.0",
|
|
114
78
|
"@elice/blocks": "^1.220803.0",
|
|
115
79
|
"@elice/design-tokens": "^1.220803.0",
|
|
116
80
|
"@elice/icons": "^1.220803.0",
|
|
117
81
|
"@elice/markdown": "^1.220803.0",
|
|
118
|
-
"@elice/material-shared-types": "1.
|
|
119
|
-
"@elice/material-shared-utils": "1.
|
|
82
|
+
"@elice/material-shared-types": "1.230111.0-mobexercise.1",
|
|
83
|
+
"@elice/material-shared-utils": "1.230111.0-mobexercise.1",
|
|
120
84
|
"@elice/types": "1.221217.0",
|
|
121
85
|
"@elice/websocket": "^1.220803.0",
|
|
122
86
|
"@types/classnames": "^2.3.1",
|
|
@@ -138,5 +102,5 @@
|
|
|
138
102
|
"recoil": "^0.6.1",
|
|
139
103
|
"styled-components": "^5.2.0"
|
|
140
104
|
},
|
|
141
|
-
"gitHead": "
|
|
105
|
+
"gitHead": "8d7bcbe80aecd631228336b1ab73bfc4d2c751cb"
|
|
142
106
|
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
export interface ExerciseMobileFileEditorProps {
|
|
3
|
-
exerciseImageId: number;
|
|
4
|
-
exerciseRoomId: number;
|
|
5
|
-
filename: string;
|
|
6
|
-
}
|
|
7
|
-
declare const ExerciseMobileFileEditor: React.FC<ExerciseMobileFileEditorProps>;
|
|
8
|
-
export default ExerciseMobileFileEditor;
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
export interface ExerciseMobileFileEditorProps {
|
|
3
|
-
exerciseImageId: number;
|
|
4
|
-
exerciseRoomId: number;
|
|
5
|
-
filename: string;
|
|
6
|
-
}
|
|
7
|
-
declare const ExerciseMobileFileEditor: React.FC<ExerciseMobileFileEditorProps>;
|
|
8
|
-
export default ExerciseMobileFileEditor;
|