@elice/material-exercise 1.260529.0 → 1.260609.0-increasesurveyoptionmax.0
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/exercise-file-editor/ExerciseFileEditor.js +4 -0
- package/cjs/components/shared/monaco-editor/MonacoEditor.d.ts +5 -0
- package/cjs/components/shared/monaco-editor/MonacoEditor.js +6 -1
- package/cjs/components/shared/monaco-editor/editor-hooks/index.d.ts +1 -0
- package/cjs/components/shared/monaco-editor/editor-hooks/index.js +2 -0
- package/cjs/components/shared/monaco-editor/editor-hooks/useMonacoClipboardGuard.d.ts +15 -0
- package/cjs/components/shared/monaco-editor/editor-hooks/useMonacoClipboardGuard.js +121 -0
- package/cjs/components/shared/monaco-editor/hooks/useEditorOptions.d.ts +1 -1
- package/cjs/components/shared/monaco-editor/hooks/useEditorOptions.js +6 -1
- package/es/components/material-exercise/exercise-file-editor/ExerciseFileEditor.js +4 -0
- package/es/components/shared/monaco-editor/MonacoEditor.d.ts +5 -0
- package/es/components/shared/monaco-editor/MonacoEditor.js +6 -1
- package/es/components/shared/monaco-editor/editor-hooks/index.d.ts +1 -0
- package/es/components/shared/monaco-editor/editor-hooks/index.js +1 -0
- package/es/components/shared/monaco-editor/editor-hooks/useMonacoClipboardGuard.d.ts +15 -0
- package/es/components/shared/monaco-editor/editor-hooks/useMonacoClipboardGuard.js +99 -0
- package/es/components/shared/monaco-editor/hooks/useEditorOptions.d.ts +1 -1
- package/es/components/shared/monaco-editor/hooks/useEditorOptions.js +6 -1
- package/package.json +10 -7
|
@@ -37,6 +37,7 @@ var React__default = /*#__PURE__*/_interopDefaultCompat(React);
|
|
|
37
37
|
*/
|
|
38
38
|
var ExerciseFileEditor = function ExerciseFileEditor(_ref) {
|
|
39
39
|
var __intl = _ref.__intl;
|
|
40
|
+
var _a, _b, _c;
|
|
40
41
|
var _React$useContext = React__default.default.useContext(context.ExerciseContext),
|
|
41
42
|
materialExerciseId = _React$useContext.materialExerciseId,
|
|
42
43
|
exerciseRoomId = _React$useContext.exerciseRoomId,
|
|
@@ -50,6 +51,8 @@ var ExerciseFileEditor = function ExerciseFileEditor(_ref) {
|
|
|
50
51
|
var preference = recoil.useRecoilValue(recoil$1.exerciseEditorPreferenceState);
|
|
51
52
|
var activeFilename = recoil.useRecoilValue(recoil$1.exerciseActiveFilenameState);
|
|
52
53
|
var readOnly = readOnlyEditor || readOnlyActiveFile;
|
|
54
|
+
// Block clipboard when the lecture's cheat config requires it (server-driven).
|
|
55
|
+
var disableClipboard = (_c = (_b = (_a = lecture === null || lecture === void 0 ? void 0 : lecture.cheatInfo) === null || _a === void 0 ? void 0 : _a.detectionInfo) === null || _b === void 0 ? void 0 : _b.isCopyAndPasteBlocked) !== null && _c !== void 0 ? _c : false;
|
|
53
56
|
var setUsercodeWebSocketState = recoil.useSetRecoilState(recoil$1.exerciseWebsocketQuery('usercodeEdit'));
|
|
54
57
|
var _React$useState = React__default.default.useState(null),
|
|
55
58
|
_React$useState2 = _rollupPluginBabelHelpers.slicedToArray(_React$useState, 2),
|
|
@@ -464,6 +467,7 @@ var ExerciseFileEditor = function ExerciseFileEditor(_ref) {
|
|
|
464
467
|
preference: Object.assign(Object.assign({}, preference), {
|
|
465
468
|
readOnly: readOnly
|
|
466
469
|
}),
|
|
470
|
+
disableClipboard: disableClipboard,
|
|
467
471
|
noLanguageIntellisense: (lecture === null || lecture === void 0 ? void 0 : lecture.lectureType) === types.enums.LectureType.Test,
|
|
468
472
|
locale: locale,
|
|
469
473
|
ref: editorApis,
|
|
@@ -61,6 +61,11 @@ export interface MonacoEditorProps {
|
|
|
61
61
|
height?: React.CSSProperties['height'];
|
|
62
62
|
/** Disable intellisense of editor. */
|
|
63
63
|
noLanguageIntellisense?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Block copy/cut/paste via keyboard, mouse (right-click), drag&drop and
|
|
66
|
+
* command palette. For exam environment. Expected to be fixed at mount.
|
|
67
|
+
*/
|
|
68
|
+
disableClipboard?: boolean;
|
|
64
69
|
/** Locale of editor. */
|
|
65
70
|
locale?: string;
|
|
66
71
|
/** On editor initialized. */
|
|
@@ -14,6 +14,7 @@ var useEditorOptions = require('./hooks/useEditorOptions.js');
|
|
|
14
14
|
var monacoLanguage = require('./utils/monacoLanguage.js');
|
|
15
15
|
var styles = require('./vendors/monaco-collab-ext/styles.js');
|
|
16
16
|
var useMonacoOptions = require('./editor-hooks/useMonacoOptions.js');
|
|
17
|
+
var useMonacoClipboardGuard = require('./editor-hooks/useMonacoClipboardGuard.js');
|
|
17
18
|
var useMonacoTheme = require('./editor-hooks/useMonacoTheme.js');
|
|
18
19
|
var preferences = require('./constants/monaco/preferences.js');
|
|
19
20
|
var useMonacoEventChange = require('./editor-hooks/useMonacoEventChange.js');
|
|
@@ -86,6 +87,8 @@ var MonacoEditor = React.forwardRef(function (_ref, ref) {
|
|
|
86
87
|
height = _ref$height === void 0 ? '100%' : _ref$height,
|
|
87
88
|
_ref$noLanguageIntell = _ref.noLanguageIntellisense,
|
|
88
89
|
noLanguageIntellisense = _ref$noLanguageIntell === void 0 ? false : _ref$noLanguageIntell,
|
|
90
|
+
_ref$disableClipboard = _ref.disableClipboard,
|
|
91
|
+
disableClipboard = _ref$disableClipboard === void 0 ? false : _ref$disableClipboard,
|
|
89
92
|
onLoad = _ref.onLoad,
|
|
90
93
|
onChange = _ref.onChange,
|
|
91
94
|
onSave = _ref.onSave,
|
|
@@ -103,7 +106,8 @@ var MonacoEditor = React.forwardRef(function (_ref, ref) {
|
|
|
103
106
|
width: width,
|
|
104
107
|
height: height,
|
|
105
108
|
preference: preference,
|
|
106
|
-
noLanguageIntellisense: noLanguageIntellisense
|
|
109
|
+
noLanguageIntellisense: noLanguageIntellisense,
|
|
110
|
+
disableClipboard: disableClipboard
|
|
107
111
|
});
|
|
108
112
|
/** Ref for preventing change event. */
|
|
109
113
|
var suppress = React__default.default.useRef(false);
|
|
@@ -292,6 +296,7 @@ var MonacoEditor = React.forwardRef(function (_ref, ref) {
|
|
|
292
296
|
// Editor hooks.
|
|
293
297
|
//
|
|
294
298
|
useMonacoOptions.useMonacoOptions(editor, editorOptions);
|
|
299
|
+
useMonacoClipboardGuard.useMonacoClipboardGuard(editor, disableClipboard, [isReady]);
|
|
295
300
|
useMonacoTheme.useMonacoTheme(preference.theme, onThemeChange);
|
|
296
301
|
useMonacoEventChange.useMonacoEventChange(editor, onChange, suppress, [isReady]);
|
|
297
302
|
useMonacoEventCursor.useMonacoEventCursor(editor, onCursor, [isReady]);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var useMonacoClipboardGuard = require('./useMonacoClipboardGuard.js');
|
|
3
4
|
var useMonacoEventChange = require('./useMonacoEventChange.js');
|
|
4
5
|
var useMonacoEventCursor = require('./useMonacoEventCursor.js');
|
|
5
6
|
var useMonacoEventScroll = require('./useMonacoEventScroll.js');
|
|
@@ -9,6 +10,7 @@ var useMonacoTheme = require('./useMonacoTheme.js');
|
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
|
|
13
|
+
exports.useMonacoClipboardGuard = useMonacoClipboardGuard.useMonacoClipboardGuard;
|
|
12
14
|
exports.useMonacoEventChange = useMonacoEventChange.useMonacoEventChange;
|
|
13
15
|
exports.useMonacoEventCursor = useMonacoEventCursor.useMonacoEventCursor;
|
|
14
16
|
exports.useMonacoEventScroll = useMonacoEventScroll.useMonacoEventScroll;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
|
|
2
|
+
import type React from 'react';
|
|
3
|
+
type Monaco = React.MutableRefObject<monaco.editor.IStandaloneCodeEditor | null>;
|
|
4
|
+
/**
|
|
5
|
+
* Hook for blocking copy/cut/paste of monaco editor in exam environment.
|
|
6
|
+
*
|
|
7
|
+
* Blocks every entry point:
|
|
8
|
+
* - keyboard shortcuts (Ctrl/Cmd+C/V/X, Shift+Insert, Ctrl+Insert, Shift+Delete)
|
|
9
|
+
* - native DOM clipboard/drag events (mouse, right-click, browser edit menu)
|
|
10
|
+
* - command palette clipboard actions (async-clipboard path that skips DOM events)
|
|
11
|
+
*
|
|
12
|
+
* @note `disabled` is expected to be fixed at mount.
|
|
13
|
+
*/
|
|
14
|
+
export declare const useMonacoClipboardGuard: (monaco: Monaco, disabled: boolean, deps?: unknown[]) => void;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var _rollupPluginBabelHelpers = require('../../../../_virtual/_rollupPluginBabelHelpers.js');
|
|
4
|
+
var React = require('react');
|
|
5
|
+
var monaco = require('monaco-editor/esm/vs/editor/editor.api');
|
|
6
|
+
|
|
7
|
+
function _interopNamespaceCompat(e) {
|
|
8
|
+
if (e && typeof e === 'object' && 'default' in e) return e;
|
|
9
|
+
var n = Object.create(null);
|
|
10
|
+
if (e) {
|
|
11
|
+
Object.keys(e).forEach(function (k) {
|
|
12
|
+
if (k !== 'default') {
|
|
13
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
14
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
get: function () { return e[k]; }
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
n.default = e;
|
|
22
|
+
return Object.freeze(n);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
var monaco__namespace = /*#__PURE__*/_interopNamespaceCompat(monaco);
|
|
26
|
+
|
|
27
|
+
var EMPTY_DEPS = [];
|
|
28
|
+
/** Keyboard shortcuts that copy/cut/paste, neutralized in exam environment. */
|
|
29
|
+
var BLOCKED_KEYBINDINGS = [monaco__namespace.KeyMod.CtrlCmd | monaco__namespace.KeyCode.KeyC,
|
|
30
|
+
// copy
|
|
31
|
+
monaco__namespace.KeyMod.CtrlCmd | monaco__namespace.KeyCode.KeyX,
|
|
32
|
+
// cut
|
|
33
|
+
monaco__namespace.KeyMod.CtrlCmd | monaco__namespace.KeyCode.KeyV,
|
|
34
|
+
// paste
|
|
35
|
+
monaco__namespace.KeyMod.CtrlCmd | monaco__namespace.KeyCode.Insert,
|
|
36
|
+
// copy (win)
|
|
37
|
+
monaco__namespace.KeyMod.Shift | monaco__namespace.KeyCode.Insert,
|
|
38
|
+
// paste (win)
|
|
39
|
+
monaco__namespace.KeyMod.Shift | monaco__namespace.KeyCode.Delete // cut (win)
|
|
40
|
+
];
|
|
41
|
+
/**
|
|
42
|
+
* Built-in clipboard actions reachable from the command palette. These run
|
|
43
|
+
* through the async-clipboard path which bypasses the native DOM events below,
|
|
44
|
+
* so they must be neutralized separately (re-registering the same id overrides
|
|
45
|
+
* the built-in action for this editor instance).
|
|
46
|
+
*/
|
|
47
|
+
var BLOCKED_ACTIONS = [{
|
|
48
|
+
id: 'editor.action.clipboardCopyAction',
|
|
49
|
+
label: 'Copy'
|
|
50
|
+
}, {
|
|
51
|
+
id: 'editor.action.clipboardCutAction',
|
|
52
|
+
label: 'Cut'
|
|
53
|
+
}, {
|
|
54
|
+
id: 'editor.action.clipboardPasteAction',
|
|
55
|
+
label: 'Paste'
|
|
56
|
+
}];
|
|
57
|
+
/** Native DOM events fired through the editor's hidden textarea. */
|
|
58
|
+
var BLOCKED_DOM_EVENTS = ['copy', 'cut', 'paste', 'contextmenu', 'dragstart', 'drop'];
|
|
59
|
+
/** `beforeinput` types that smuggle clipboard/drag content into the editor. */
|
|
60
|
+
var BLOCKED_INPUT_TYPES = ['insertFromPaste', 'insertFromDrop', 'deleteByCut'];
|
|
61
|
+
var noop = function noop() {
|
|
62
|
+
// intentionally do nothing.
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Hook for blocking copy/cut/paste of monaco editor in exam environment.
|
|
66
|
+
*
|
|
67
|
+
* Blocks every entry point:
|
|
68
|
+
* - keyboard shortcuts (Ctrl/Cmd+C/V/X, Shift+Insert, Ctrl+Insert, Shift+Delete)
|
|
69
|
+
* - native DOM clipboard/drag events (mouse, right-click, browser edit menu)
|
|
70
|
+
* - command palette clipboard actions (async-clipboard path that skips DOM events)
|
|
71
|
+
*
|
|
72
|
+
* @note `disabled` is expected to be fixed at mount.
|
|
73
|
+
*/
|
|
74
|
+
var useMonacoClipboardGuard = function useMonacoClipboardGuard(monaco, disabled) {
|
|
75
|
+
var deps = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : EMPTY_DEPS;
|
|
76
|
+
React.useEffect(function () {
|
|
77
|
+
var editor = monaco.current;
|
|
78
|
+
if (!disabled || !editor) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// 1. neutralize clipboard keyboard shortcuts.
|
|
82
|
+
BLOCKED_KEYBINDINGS.forEach(function (keybinding) {
|
|
83
|
+
return editor.addCommand(keybinding, noop);
|
|
84
|
+
});
|
|
85
|
+
// 2. neutralize clipboard actions reachable from the command palette.
|
|
86
|
+
BLOCKED_ACTIONS.forEach(function (_ref) {
|
|
87
|
+
var id = _ref.id,
|
|
88
|
+
label = _ref.label;
|
|
89
|
+
return editor.addAction({
|
|
90
|
+
id: id,
|
|
91
|
+
label: label,
|
|
92
|
+
run: noop
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
// 3. block native clipboard/drag DOM events at the editor container.
|
|
96
|
+
var node = editor.getContainerDomNode();
|
|
97
|
+
var blockEvent = function blockEvent(event) {
|
|
98
|
+
event.preventDefault();
|
|
99
|
+
event.stopPropagation();
|
|
100
|
+
};
|
|
101
|
+
var blockBeforeInput = function blockBeforeInput(event) {
|
|
102
|
+
if (BLOCKED_INPUT_TYPES.includes(event.inputType)) {
|
|
103
|
+
event.preventDefault();
|
|
104
|
+
event.stopPropagation();
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
BLOCKED_DOM_EVENTS.forEach(function (type) {
|
|
108
|
+
return node.addEventListener(type, blockEvent, true);
|
|
109
|
+
});
|
|
110
|
+
node.addEventListener('beforeinput', blockBeforeInput, true);
|
|
111
|
+
return function () {
|
|
112
|
+
BLOCKED_DOM_EVENTS.forEach(function (type) {
|
|
113
|
+
return node.removeEventListener(type, blockEvent, true);
|
|
114
|
+
});
|
|
115
|
+
node.removeEventListener('beforeinput', blockBeforeInput, true);
|
|
116
|
+
};
|
|
117
|
+
}, // eslint-disable-next-line react-hooks/exhaustive-deps
|
|
118
|
+
[monaco, disabled].concat(_rollupPluginBabelHelpers.toConsumableArray(deps)));
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
exports.useMonacoClipboardGuard = useMonacoClipboardGuard;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type monaco from 'monaco-editor/esm/vs/editor/editor.api';
|
|
2
2
|
import type { MonacoEditorProps } from '../MonacoEditor';
|
|
3
3
|
export type EditorOptions = Parameters<monaco.editor.IStandaloneCodeEditor['updateOptions']>[0];
|
|
4
|
-
export type UseEditorOptionsProps = Required<Pick<MonacoEditorProps, 'width' | 'height' | 'preference' | 'noLanguageIntellisense'>>;
|
|
4
|
+
export type UseEditorOptionsProps = Required<Pick<MonacoEditorProps, 'width' | 'height' | 'preference' | 'noLanguageIntellisense' | 'disableClipboard'>>;
|
|
5
5
|
/**
|
|
6
6
|
* Hook to get editor options from props.
|
|
7
7
|
*/
|
|
@@ -67,7 +67,12 @@ var getEditorOptions = function getEditorOptions(props) {
|
|
|
67
67
|
wordBasedSuggestions: true,
|
|
68
68
|
wordBasedSuggestionsOnlySameLanguage: true,
|
|
69
69
|
// etc
|
|
70
|
-
automaticLayout: props.width === '100%' || props.height === '100%'
|
|
70
|
+
automaticLayout: props.width === '100%' || props.height === '100%',
|
|
71
|
+
// exam: disable clipboard-related affordances.
|
|
72
|
+
// right-click menu, text drag&drop, and linux middle-click (primary selection) paste.
|
|
73
|
+
contextmenu: !props.disableClipboard,
|
|
74
|
+
dragAndDrop: !props.disableClipboard,
|
|
75
|
+
selectionClipboard: !props.disableClipboard
|
|
71
76
|
});
|
|
72
77
|
};
|
|
73
78
|
/**
|
|
@@ -29,6 +29,7 @@ import { checkExerciseFileResettable } from '../../../utils/exerciseFile.js';
|
|
|
29
29
|
*/
|
|
30
30
|
var ExerciseFileEditor = function ExerciseFileEditor(_ref) {
|
|
31
31
|
var __intl = _ref.__intl;
|
|
32
|
+
var _a, _b, _c;
|
|
32
33
|
var _React$useContext = React.useContext(ExerciseContext),
|
|
33
34
|
materialExerciseId = _React$useContext.materialExerciseId,
|
|
34
35
|
exerciseRoomId = _React$useContext.exerciseRoomId,
|
|
@@ -42,6 +43,8 @@ var ExerciseFileEditor = function ExerciseFileEditor(_ref) {
|
|
|
42
43
|
var preference = useRecoilValue(exerciseEditorPreferenceState);
|
|
43
44
|
var activeFilename = useRecoilValue(exerciseActiveFilenameState);
|
|
44
45
|
var readOnly = readOnlyEditor || readOnlyActiveFile;
|
|
46
|
+
// Block clipboard when the lecture's cheat config requires it (server-driven).
|
|
47
|
+
var disableClipboard = (_c = (_b = (_a = lecture === null || lecture === void 0 ? void 0 : lecture.cheatInfo) === null || _a === void 0 ? void 0 : _a.detectionInfo) === null || _b === void 0 ? void 0 : _b.isCopyAndPasteBlocked) !== null && _c !== void 0 ? _c : false;
|
|
45
48
|
var setUsercodeWebSocketState = useSetRecoilState(exerciseWebsocketQuery('usercodeEdit'));
|
|
46
49
|
var _React$useState = React.useState(null),
|
|
47
50
|
_React$useState2 = _slicedToArray(_React$useState, 2),
|
|
@@ -456,6 +459,7 @@ var ExerciseFileEditor = function ExerciseFileEditor(_ref) {
|
|
|
456
459
|
preference: Object.assign(Object.assign({}, preference), {
|
|
457
460
|
readOnly: readOnly
|
|
458
461
|
}),
|
|
462
|
+
disableClipboard: disableClipboard,
|
|
459
463
|
noLanguageIntellisense: (lecture === null || lecture === void 0 ? void 0 : lecture.lectureType) === enums.LectureType.Test,
|
|
460
464
|
locale: locale,
|
|
461
465
|
ref: editorApis,
|
|
@@ -61,6 +61,11 @@ export interface MonacoEditorProps {
|
|
|
61
61
|
height?: React.CSSProperties['height'];
|
|
62
62
|
/** Disable intellisense of editor. */
|
|
63
63
|
noLanguageIntellisense?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Block copy/cut/paste via keyboard, mouse (right-click), drag&drop and
|
|
66
|
+
* command palette. For exam environment. Expected to be fixed at mount.
|
|
67
|
+
*/
|
|
68
|
+
disableClipboard?: boolean;
|
|
64
69
|
/** Locale of editor. */
|
|
65
70
|
locale?: string;
|
|
66
71
|
/** On editor initialized. */
|
|
@@ -10,6 +10,7 @@ import { useEditorOptions } from './hooks/useEditorOptions.js';
|
|
|
10
10
|
import { getLanguageFromFilename } from './utils/monacoLanguage.js';
|
|
11
11
|
import { cssRemoteMarker } from './vendors/monaco-collab-ext/styles.js';
|
|
12
12
|
import { useMonacoOptions } from './editor-hooks/useMonacoOptions.js';
|
|
13
|
+
import { useMonacoClipboardGuard } from './editor-hooks/useMonacoClipboardGuard.js';
|
|
13
14
|
import { useMonacoTheme } from './editor-hooks/useMonacoTheme.js';
|
|
14
15
|
import { DEFAULT_MONACO_EDITOR_PREFERENCE } from './constants/monaco/preferences.js';
|
|
15
16
|
import { useMonacoEventChange } from './editor-hooks/useMonacoEventChange.js';
|
|
@@ -58,6 +59,8 @@ var MonacoEditor = forwardRef(function (_ref, ref) {
|
|
|
58
59
|
height = _ref$height === void 0 ? '100%' : _ref$height,
|
|
59
60
|
_ref$noLanguageIntell = _ref.noLanguageIntellisense,
|
|
60
61
|
noLanguageIntellisense = _ref$noLanguageIntell === void 0 ? false : _ref$noLanguageIntell,
|
|
62
|
+
_ref$disableClipboard = _ref.disableClipboard,
|
|
63
|
+
disableClipboard = _ref$disableClipboard === void 0 ? false : _ref$disableClipboard,
|
|
61
64
|
onLoad = _ref.onLoad,
|
|
62
65
|
onChange = _ref.onChange,
|
|
63
66
|
onSave = _ref.onSave,
|
|
@@ -75,7 +78,8 @@ var MonacoEditor = forwardRef(function (_ref, ref) {
|
|
|
75
78
|
width: width,
|
|
76
79
|
height: height,
|
|
77
80
|
preference: preference,
|
|
78
|
-
noLanguageIntellisense: noLanguageIntellisense
|
|
81
|
+
noLanguageIntellisense: noLanguageIntellisense,
|
|
82
|
+
disableClipboard: disableClipboard
|
|
79
83
|
});
|
|
80
84
|
/** Ref for preventing change event. */
|
|
81
85
|
var suppress = React.useRef(false);
|
|
@@ -264,6 +268,7 @@ var MonacoEditor = forwardRef(function (_ref, ref) {
|
|
|
264
268
|
// Editor hooks.
|
|
265
269
|
//
|
|
266
270
|
useMonacoOptions(editor, editorOptions);
|
|
271
|
+
useMonacoClipboardGuard(editor, disableClipboard, [isReady]);
|
|
267
272
|
useMonacoTheme(preference.theme, onThemeChange);
|
|
268
273
|
useMonacoEventChange(editor, onChange, suppress, [isReady]);
|
|
269
274
|
useMonacoEventCursor(editor, onCursor, [isReady]);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { useMonacoClipboardGuard } from './useMonacoClipboardGuard.js';
|
|
1
2
|
export { useMonacoEventChange } from './useMonacoEventChange.js';
|
|
2
3
|
export { useMonacoEventCursor } from './useMonacoEventCursor.js';
|
|
3
4
|
export { useMonacoEventScroll } from './useMonacoEventScroll.js';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
|
|
2
|
+
import type React from 'react';
|
|
3
|
+
type Monaco = React.MutableRefObject<monaco.editor.IStandaloneCodeEditor | null>;
|
|
4
|
+
/**
|
|
5
|
+
* Hook for blocking copy/cut/paste of monaco editor in exam environment.
|
|
6
|
+
*
|
|
7
|
+
* Blocks every entry point:
|
|
8
|
+
* - keyboard shortcuts (Ctrl/Cmd+C/V/X, Shift+Insert, Ctrl+Insert, Shift+Delete)
|
|
9
|
+
* - native DOM clipboard/drag events (mouse, right-click, browser edit menu)
|
|
10
|
+
* - command palette clipboard actions (async-clipboard path that skips DOM events)
|
|
11
|
+
*
|
|
12
|
+
* @note `disabled` is expected to be fixed at mount.
|
|
13
|
+
*/
|
|
14
|
+
export declare const useMonacoClipboardGuard: (monaco: Monaco, disabled: boolean, deps?: unknown[]) => void;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { toConsumableArray as _toConsumableArray } from '../../../../_virtual/_rollupPluginBabelHelpers.js';
|
|
2
|
+
import { useEffect } from 'react';
|
|
3
|
+
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
|
|
4
|
+
|
|
5
|
+
var EMPTY_DEPS = [];
|
|
6
|
+
/** Keyboard shortcuts that copy/cut/paste, neutralized in exam environment. */
|
|
7
|
+
var BLOCKED_KEYBINDINGS = [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyC,
|
|
8
|
+
// copy
|
|
9
|
+
monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyX,
|
|
10
|
+
// cut
|
|
11
|
+
monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyV,
|
|
12
|
+
// paste
|
|
13
|
+
monaco.KeyMod.CtrlCmd | monaco.KeyCode.Insert,
|
|
14
|
+
// copy (win)
|
|
15
|
+
monaco.KeyMod.Shift | monaco.KeyCode.Insert,
|
|
16
|
+
// paste (win)
|
|
17
|
+
monaco.KeyMod.Shift | monaco.KeyCode.Delete // cut (win)
|
|
18
|
+
];
|
|
19
|
+
/**
|
|
20
|
+
* Built-in clipboard actions reachable from the command palette. These run
|
|
21
|
+
* through the async-clipboard path which bypasses the native DOM events below,
|
|
22
|
+
* so they must be neutralized separately (re-registering the same id overrides
|
|
23
|
+
* the built-in action for this editor instance).
|
|
24
|
+
*/
|
|
25
|
+
var BLOCKED_ACTIONS = [{
|
|
26
|
+
id: 'editor.action.clipboardCopyAction',
|
|
27
|
+
label: 'Copy'
|
|
28
|
+
}, {
|
|
29
|
+
id: 'editor.action.clipboardCutAction',
|
|
30
|
+
label: 'Cut'
|
|
31
|
+
}, {
|
|
32
|
+
id: 'editor.action.clipboardPasteAction',
|
|
33
|
+
label: 'Paste'
|
|
34
|
+
}];
|
|
35
|
+
/** Native DOM events fired through the editor's hidden textarea. */
|
|
36
|
+
var BLOCKED_DOM_EVENTS = ['copy', 'cut', 'paste', 'contextmenu', 'dragstart', 'drop'];
|
|
37
|
+
/** `beforeinput` types that smuggle clipboard/drag content into the editor. */
|
|
38
|
+
var BLOCKED_INPUT_TYPES = ['insertFromPaste', 'insertFromDrop', 'deleteByCut'];
|
|
39
|
+
var noop = function noop() {
|
|
40
|
+
// intentionally do nothing.
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Hook for blocking copy/cut/paste of monaco editor in exam environment.
|
|
44
|
+
*
|
|
45
|
+
* Blocks every entry point:
|
|
46
|
+
* - keyboard shortcuts (Ctrl/Cmd+C/V/X, Shift+Insert, Ctrl+Insert, Shift+Delete)
|
|
47
|
+
* - native DOM clipboard/drag events (mouse, right-click, browser edit menu)
|
|
48
|
+
* - command palette clipboard actions (async-clipboard path that skips DOM events)
|
|
49
|
+
*
|
|
50
|
+
* @note `disabled` is expected to be fixed at mount.
|
|
51
|
+
*/
|
|
52
|
+
var useMonacoClipboardGuard = function useMonacoClipboardGuard(monaco, disabled) {
|
|
53
|
+
var deps = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : EMPTY_DEPS;
|
|
54
|
+
useEffect(function () {
|
|
55
|
+
var editor = monaco.current;
|
|
56
|
+
if (!disabled || !editor) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
// 1. neutralize clipboard keyboard shortcuts.
|
|
60
|
+
BLOCKED_KEYBINDINGS.forEach(function (keybinding) {
|
|
61
|
+
return editor.addCommand(keybinding, noop);
|
|
62
|
+
});
|
|
63
|
+
// 2. neutralize clipboard actions reachable from the command palette.
|
|
64
|
+
BLOCKED_ACTIONS.forEach(function (_ref) {
|
|
65
|
+
var id = _ref.id,
|
|
66
|
+
label = _ref.label;
|
|
67
|
+
return editor.addAction({
|
|
68
|
+
id: id,
|
|
69
|
+
label: label,
|
|
70
|
+
run: noop
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
// 3. block native clipboard/drag DOM events at the editor container.
|
|
74
|
+
var node = editor.getContainerDomNode();
|
|
75
|
+
var blockEvent = function blockEvent(event) {
|
|
76
|
+
event.preventDefault();
|
|
77
|
+
event.stopPropagation();
|
|
78
|
+
};
|
|
79
|
+
var blockBeforeInput = function blockBeforeInput(event) {
|
|
80
|
+
if (BLOCKED_INPUT_TYPES.includes(event.inputType)) {
|
|
81
|
+
event.preventDefault();
|
|
82
|
+
event.stopPropagation();
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
BLOCKED_DOM_EVENTS.forEach(function (type) {
|
|
86
|
+
return node.addEventListener(type, blockEvent, true);
|
|
87
|
+
});
|
|
88
|
+
node.addEventListener('beforeinput', blockBeforeInput, true);
|
|
89
|
+
return function () {
|
|
90
|
+
BLOCKED_DOM_EVENTS.forEach(function (type) {
|
|
91
|
+
return node.removeEventListener(type, blockEvent, true);
|
|
92
|
+
});
|
|
93
|
+
node.removeEventListener('beforeinput', blockBeforeInput, true);
|
|
94
|
+
};
|
|
95
|
+
}, // eslint-disable-next-line react-hooks/exhaustive-deps
|
|
96
|
+
[monaco, disabled].concat(_toConsumableArray(deps)));
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export { useMonacoClipboardGuard };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type monaco from 'monaco-editor/esm/vs/editor/editor.api';
|
|
2
2
|
import type { MonacoEditorProps } from '../MonacoEditor';
|
|
3
3
|
export type EditorOptions = Parameters<monaco.editor.IStandaloneCodeEditor['updateOptions']>[0];
|
|
4
|
-
export type UseEditorOptionsProps = Required<Pick<MonacoEditorProps, 'width' | 'height' | 'preference' | 'noLanguageIntellisense'>>;
|
|
4
|
+
export type UseEditorOptionsProps = Required<Pick<MonacoEditorProps, 'width' | 'height' | 'preference' | 'noLanguageIntellisense' | 'disableClipboard'>>;
|
|
5
5
|
/**
|
|
6
6
|
* Hook to get editor options from props.
|
|
7
7
|
*/
|
|
@@ -65,7 +65,12 @@ var getEditorOptions = function getEditorOptions(props) {
|
|
|
65
65
|
wordBasedSuggestions: true,
|
|
66
66
|
wordBasedSuggestionsOnlySameLanguage: true,
|
|
67
67
|
// etc
|
|
68
|
-
automaticLayout: props.width === '100%' || props.height === '100%'
|
|
68
|
+
automaticLayout: props.width === '100%' || props.height === '100%',
|
|
69
|
+
// exam: disable clipboard-related affordances.
|
|
70
|
+
// right-click menu, text drag&drop, and linux middle-click (primary selection) paste.
|
|
71
|
+
contextmenu: !props.disableClipboard,
|
|
72
|
+
dragAndDrop: !props.disableClipboard,
|
|
73
|
+
selectionClipboard: !props.disableClipboard
|
|
69
74
|
});
|
|
70
75
|
};
|
|
71
76
|
/**
|
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elice/material-exercise",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.260609.0-increasesurveyoptionmax.0",
|
|
4
4
|
"description": "User view and editing components of Elice material exercise",
|
|
5
|
-
"repository":
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://git.elicer.io/elice/frontend/library/elice-material"
|
|
8
|
+
},
|
|
6
9
|
"license": "UNLICENSED",
|
|
7
10
|
"main": "./cjs/index.js",
|
|
8
11
|
"module": "./es/index.js",
|
|
@@ -39,8 +42,8 @@
|
|
|
39
42
|
"overlay-kit": "^1.8.0",
|
|
40
43
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
|
41
44
|
"react-use": "^17.0.0",
|
|
42
|
-
"@elice/material-shared-types": "1.
|
|
43
|
-
"@elice/material-shared-utils": "1.
|
|
45
|
+
"@elice/material-shared-types": "1.260609.0-increasesurveyoptionmax.0",
|
|
46
|
+
"@elice/material-shared-utils": "1.260609.0-increasesurveyoptionmax.0"
|
|
44
47
|
},
|
|
45
48
|
"dependencies": {
|
|
46
49
|
"@novnc/novnc": "^1.3.0",
|
|
@@ -80,7 +83,7 @@
|
|
|
80
83
|
"@elice/mui-elements": "5.251204.0",
|
|
81
84
|
"@elice/mui-system": "5.251204.0",
|
|
82
85
|
"@elice/rollup-config": "^1.260406.0",
|
|
83
|
-
"@elice/types": "1.
|
|
86
|
+
"@elice/types": "1.260608.0",
|
|
84
87
|
"@elice/utils": "^1.250930.0",
|
|
85
88
|
"@elice/websocket": "^1.220815.0",
|
|
86
89
|
"@emotion/babel-plugin": "^11.13.5",
|
|
@@ -115,8 +118,8 @@
|
|
|
115
118
|
"rollup": "^4.0.0",
|
|
116
119
|
"typescript": "~5.9.3",
|
|
117
120
|
"vite": "^4.4.9",
|
|
118
|
-
"@elice/material-shared-types": "1.
|
|
119
|
-
"@elice/material-shared-utils": "1.
|
|
121
|
+
"@elice/material-shared-types": "1.260609.0-increasesurveyoptionmax.0",
|
|
122
|
+
"@elice/material-shared-utils": "1.260609.0-increasesurveyoptionmax.0"
|
|
120
123
|
},
|
|
121
124
|
"scripts": {
|
|
122
125
|
"start": "run-s watch",
|