@eccenca/gui-elements 23.3.1 → 23.4.0-rc.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/CHANGELOG.md +14 -0
- package/dist/cjs/components/AutoSuggestion/AutoSuggestion.js +61 -19
- package/dist/cjs/components/AutoSuggestion/AutoSuggestion.js.map +1 -1
- package/dist/cjs/components/AutoSuggestion/AutoSuggestionList.js +4 -18
- package/dist/cjs/components/AutoSuggestion/AutoSuggestionList.js.map +1 -1
- package/dist/cjs/components/AutoSuggestion/ExtendedCodeEditor.js +65 -0
- package/dist/cjs/components/AutoSuggestion/ExtendedCodeEditor.js.map +1 -0
- package/dist/cjs/components/AutoSuggestion/index.js +3 -3
- package/dist/cjs/components/AutoSuggestion/index.js.map +1 -1
- package/dist/cjs/components/Card/CardHeader.js +19 -18
- package/dist/cjs/components/Card/CardHeader.js.map +1 -1
- package/dist/cjs/components/Grid/GridColumn.js +3 -3
- package/dist/cjs/components/Grid/GridColumn.js.map +1 -1
- package/dist/cjs/components/PropertyValuePair/PropertyName.js +4 -2
- package/dist/cjs/components/PropertyValuePair/PropertyName.js.map +1 -1
- package/dist/cjs/components/PropertyValuePair/PropertyValue.js +6 -4
- package/dist/cjs/components/PropertyValuePair/PropertyValue.js.map +1 -1
- package/dist/cjs/components/PropertyValuePair/PropertyValuePair.js +13 -3
- package/dist/cjs/components/PropertyValuePair/PropertyValuePair.js.map +1 -1
- package/dist/cjs/components/Skeleton/Skeleton.js +30 -0
- package/dist/cjs/components/Skeleton/Skeleton.js.map +1 -0
- package/dist/cjs/components/Skeleton/classnames.js +6 -0
- package/dist/cjs/components/Skeleton/classnames.js.map +1 -0
- package/dist/cjs/components/Table/TableCell.js +3 -2
- package/dist/cjs/components/Table/TableCell.js.map +1 -1
- package/dist/cjs/components/TextField/useTextValidation.js.map +1 -1
- package/dist/cjs/components/index.js +1 -0
- package/dist/cjs/components/index.js.map +1 -1
- package/dist/cjs/index.js +2 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/components/AutoSuggestion/AutoSuggestion.js +82 -40
- package/dist/esm/components/AutoSuggestion/AutoSuggestion.js.map +1 -1
- package/dist/esm/components/AutoSuggestion/AutoSuggestionList.js +6 -20
- package/dist/esm/components/AutoSuggestion/AutoSuggestionList.js.map +1 -1
- package/dist/esm/components/AutoSuggestion/ExtendedCodeEditor.js +70 -0
- package/dist/esm/components/AutoSuggestion/ExtendedCodeEditor.js.map +1 -0
- package/dist/esm/components/AutoSuggestion/index.js +2 -2
- package/dist/esm/components/AutoSuggestion/index.js.map +1 -1
- package/dist/esm/components/Card/CardHeader.js +15 -14
- package/dist/esm/components/Card/CardHeader.js.map +1 -1
- package/dist/esm/components/Grid/GridColumn.js +3 -3
- package/dist/esm/components/Grid/GridColumn.js.map +1 -1
- package/dist/esm/components/PropertyValuePair/PropertyName.js +4 -2
- package/dist/esm/components/PropertyValuePair/PropertyName.js.map +1 -1
- package/dist/esm/components/PropertyValuePair/PropertyValue.js +6 -4
- package/dist/esm/components/PropertyValuePair/PropertyValue.js.map +1 -1
- package/dist/esm/components/PropertyValuePair/PropertyValuePair.js +13 -3
- package/dist/esm/components/PropertyValuePair/PropertyValuePair.js.map +1 -1
- package/dist/esm/components/Skeleton/Skeleton.js +24 -0
- package/dist/esm/components/Skeleton/Skeleton.js.map +1 -0
- package/dist/esm/components/Skeleton/classnames.js +3 -0
- package/dist/esm/components/Skeleton/classnames.js.map +1 -0
- package/dist/esm/components/Table/TableCell.js +4 -3
- package/dist/esm/components/Table/TableCell.js.map +1 -1
- package/dist/esm/components/TextField/useTextValidation.js.map +1 -1
- package/dist/esm/components/index.js +1 -0
- package/dist/esm/components/index.js.map +1 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/types/components/AutoSuggestion/AutoSuggestion.d.ts +8 -2
- package/dist/types/components/AutoSuggestion/AutoSuggestionList.d.ts +6 -4
- package/dist/types/components/AutoSuggestion/{SingleLineCodeEditor.d.ts → ExtendedCodeEditor.d.ts} +7 -4
- package/dist/types/components/AutoSuggestion/index.d.ts +3 -3
- package/dist/types/components/Card/CardHeader.d.ts +2 -2
- package/dist/types/components/Grid/GridColumn.d.ts +1 -0
- package/dist/types/components/PropertyValuePair/PropertyName.d.ts +7 -1
- package/dist/types/components/PropertyValuePair/PropertyValue.d.ts +7 -1
- package/dist/types/components/PropertyValuePair/PropertyValuePair.d.ts +5 -1
- package/dist/types/components/Skeleton/Skeleton.d.ts +13 -0
- package/dist/types/components/Skeleton/classnames.d.ts +1 -0
- package/dist/types/components/Table/TableCell.d.ts +7 -2
- package/dist/types/components/TextField/useTextValidation.d.ts +1 -0
- package/dist/types/components/index.d.ts +1 -0
- package/dist/types/index.d.ts +3 -0
- package/package.json +1 -1
- package/src/components/AutoSuggestion/AutoSuggestion.tsx +84 -31
- package/src/components/AutoSuggestion/AutoSuggestionList.tsx +11 -42
- package/src/components/AutoSuggestion/ExtendedCodeEditor.tsx +129 -0
- package/src/components/AutoSuggestion/index.ts +3 -11
- package/src/components/AutoSuggestion/tests/AutoSuggestionList.test.tsx +83 -84
- package/src/components/AutoSuggestion/tests/{SingleLineCodeEditor.test.tsx → ExtendedCodeEditor.test.tsx} +7 -7
- package/src/components/Card/CardHeader.tsx +23 -25
- package/src/components/Grid/GridColumn.tsx +15 -13
- package/src/components/Pagination/pagination.scss +6 -1
- package/src/components/PropertyValuePair/PropertyName.tsx +21 -2
- package/src/components/PropertyValuePair/PropertyValue.tsx +21 -5
- package/src/components/PropertyValuePair/PropertyValuePair.tsx +23 -4
- package/src/components/PropertyValuePair/propertyvalue.scss +6 -1
- package/src/components/PropertyValuePair/stories/PropertyName.stories.tsx +18 -0
- package/src/components/PropertyValuePair/stories/PropertyValue.stories.tsx +18 -0
- package/src/components/PropertyValuePair/stories/PropertyValueList.stories.tsx +33 -0
- package/src/components/PropertyValuePair/stories/PropertyValuePair.stories.tsx +29 -0
- package/src/components/Skeleton/Skeleton.stories.tsx +29 -0
- package/src/components/Skeleton/Skeleton.tsx +32 -0
- package/src/components/Skeleton/classnames.ts +3 -0
- package/src/components/Skeleton/skeleton.scss +1 -0
- package/src/components/Table/TableCell.tsx +12 -8
- package/src/components/Table/stories/TableCell.stories.tsx +30 -0
- package/src/components/Table/table.scss +96 -50
- package/src/components/TextField/stories/TextField.stories.tsx +21 -18
- package/src/components/TextField/useTextValidation.ts +82 -68
- package/src/components/index.scss +1 -0
- package/src/components/index.ts +1 -0
- package/src/extensions/codemirror/_codemirror.scss +1 -0
- package/src/index.ts +2 -0
- package/dist/cjs/components/AutoSuggestion/SingleLineCodeEditor.js +0 -53
- package/dist/cjs/components/AutoSuggestion/SingleLineCodeEditor.js.map +0 -1
- package/dist/esm/components/AutoSuggestion/SingleLineCodeEditor.js +0 -47
- package/dist/esm/components/AutoSuggestion/SingleLineCodeEditor.js.map +0 -1
- package/src/components/AutoSuggestion/SingleLineCodeEditor.tsx +0 -110
|
@@ -2,6 +2,7 @@ import React, { ChangeEventHandler } from "react";
|
|
|
2
2
|
export interface InvisibleCharacterWarningProps {
|
|
3
3
|
/**
|
|
4
4
|
* If set, the function is called after every value change what invisible characters have been detected.
|
|
5
|
+
* The input component must be controlled for this callback to be triggered.
|
|
5
6
|
*/
|
|
6
7
|
callback: (detectedCodePoints: Set<number>) => any;
|
|
7
8
|
/**
|
|
@@ -34,6 +34,7 @@ export * from "./RadioButton/RadioButton";
|
|
|
34
34
|
export * from "./Select/Select";
|
|
35
35
|
export * from "./Separation/Divider";
|
|
36
36
|
export * from "./Separation/Spacing";
|
|
37
|
+
export * from "./Skeleton/Skeleton";
|
|
37
38
|
export * from "./Spinner/Spinner";
|
|
38
39
|
export * from "./Structure";
|
|
39
40
|
export * from "./Switch/Switch";
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
+
import * as Skeleton from "./components/Skeleton/classnames";
|
|
1
2
|
import * as TypographyClassNames from "./components/Typography/classnames";
|
|
2
3
|
import * as LegacyReplacements from "./legacy-replacements";
|
|
3
4
|
declare const ClassNames: {
|
|
5
|
+
Skeleton: typeof Skeleton;
|
|
4
6
|
Typography: typeof TypographyClassNames;
|
|
5
7
|
Intent: {
|
|
6
8
|
[key: string]: string;
|
|
7
9
|
};
|
|
8
10
|
};
|
|
9
11
|
declare const HelperClasses: {
|
|
12
|
+
Skeleton: typeof Skeleton;
|
|
10
13
|
Typography: typeof TypographyClassNames;
|
|
11
14
|
Intent: {
|
|
12
15
|
[key: string]: string;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eccenca/gui-elements",
|
|
3
3
|
"description": "GUI elements based on other libraries, usable in React application, written in Typescript.",
|
|
4
|
-
"version": "23.
|
|
4
|
+
"version": "23.4.0-rc.0",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://github.com/eccenca/gui-elements",
|
|
7
7
|
"bugs": "https://github.com/eccenca/gui-elements/issues",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useEffect, useMemo, useState } from "react";
|
|
2
2
|
import { Classes as BlueprintClassNames } from "@blueprintjs/core";
|
|
3
|
-
import CodeMirror, { Position } from "codemirror";
|
|
3
|
+
import CodeMirror, { Editor as CodeMirrorEditor, Position } from "codemirror";
|
|
4
4
|
import { debounce } from "lodash";
|
|
5
5
|
|
|
6
6
|
import { CLASSPREFIX as eccgui } from "../../configuration/constants";
|
|
@@ -8,13 +8,17 @@ import { CLASSPREFIX as eccgui } from "../../configuration/constants";
|
|
|
8
8
|
import { ContextOverlay, FieldItem, IconButton, Spinner, Toolbar, ToolbarSection } from "./../../";
|
|
9
9
|
import { AutoSuggestionList } from "./AutoSuggestionList";
|
|
10
10
|
//custom components
|
|
11
|
-
import
|
|
11
|
+
import ExtendedCodeEditor, { IRange } from "./ExtendedCodeEditor";
|
|
12
|
+
|
|
13
|
+
const LINE_COLUMN_WIDTH = 29;
|
|
14
|
+
const EXTRA_VERTICAL_PADDING = 10;
|
|
12
15
|
|
|
13
16
|
export enum OVERWRITTEN_KEYS {
|
|
14
17
|
ArrowUp = "ArrowUp",
|
|
15
18
|
ArrowDown = "ArrowDown",
|
|
16
19
|
Enter = "Enter",
|
|
17
20
|
Tab = "Tab",
|
|
21
|
+
Escape = "Escape",
|
|
18
22
|
}
|
|
19
23
|
export type OverwrittenKeyTypes = (typeof OVERWRITTEN_KEYS)[keyof typeof OVERWRITTEN_KEYS];
|
|
20
24
|
|
|
@@ -128,6 +132,12 @@ export interface AutoSuggestionProps {
|
|
|
128
132
|
/** Delay in ms before a validation request should be send after nothing is typed in anymore.
|
|
129
133
|
* This should prevent the UI to send too many requests to the backend. */
|
|
130
134
|
validationRequestDelay?: number;
|
|
135
|
+
/**
|
|
136
|
+
* multiline configuration
|
|
137
|
+
*/
|
|
138
|
+
multiline?: boolean;
|
|
139
|
+
// The editor theme, e.g. "sparql"
|
|
140
|
+
mode?: string;
|
|
131
141
|
}
|
|
132
142
|
|
|
133
143
|
// @deprecated
|
|
@@ -138,8 +148,6 @@ interface RequestMetaData {
|
|
|
138
148
|
requestId: string | undefined;
|
|
139
149
|
}
|
|
140
150
|
|
|
141
|
-
type HorizontalShiftCallbackFunction = (shift: number) => any;
|
|
142
|
-
|
|
143
151
|
/**
|
|
144
152
|
* **Element is deprecated.**
|
|
145
153
|
* Use `CodeAutocompleteField` as replacement.
|
|
@@ -165,10 +173,12 @@ export const AutoSuggestion = ({
|
|
|
165
173
|
showScrollBar = true,
|
|
166
174
|
autoCompletionRequestDelay = 1000,
|
|
167
175
|
validationRequestDelay = 200,
|
|
176
|
+
mode = "null",
|
|
177
|
+
multiline = false,
|
|
168
178
|
}: AutoSuggestionProps) => {
|
|
169
179
|
const value = React.useRef<string>(initialValue);
|
|
170
180
|
const cursorPosition = React.useRef(0);
|
|
171
|
-
const
|
|
181
|
+
const dropdownXYoffset = React.useRef<{ x: number; y: number }>({ x: 0, y: 0 });
|
|
172
182
|
const [shouldShowDropdown, setShouldShowDropdown] = React.useState(false);
|
|
173
183
|
const [suggestions, setSuggestions] = React.useState<ISuggestionWithReplacementInfo[]>([]);
|
|
174
184
|
const [suggestionsPending, setSuggestionsPending] = React.useState(false);
|
|
@@ -182,6 +192,7 @@ export const AutoSuggestion = ({
|
|
|
182
192
|
const [highlightedElement, setHighlightedElement] = useState<ISuggestionWithReplacementInfo | undefined>(undefined);
|
|
183
193
|
const [editorInstance, setEditorInstance] = React.useState<CodeMirror.Editor>();
|
|
184
194
|
const isFocused = React.useRef(false);
|
|
195
|
+
const autoSuggestionDivRef = React.useRef<HTMLDivElement>(null);
|
|
185
196
|
/** Mutable editor state, since this needs to be current in scope of the SingleLineEditorComponent. */
|
|
186
197
|
const [editorState] = React.useState<{
|
|
187
198
|
index: number;
|
|
@@ -221,9 +232,10 @@ export const AutoSuggestion = ({
|
|
|
221
232
|
const { from, length } = highlightedElement;
|
|
222
233
|
if (length > 0 && selectedTextRanges.current.length === 0) {
|
|
223
234
|
const to = from + length;
|
|
235
|
+
const cursor = editorState.editorInstance?.getCursor();
|
|
224
236
|
const marker = editorInstance.markText(
|
|
225
|
-
{ line: 0, ch: from },
|
|
226
|
-
{ line: 0, ch: to },
|
|
237
|
+
{ line: cursor?.line ?? 0, ch: from },
|
|
238
|
+
{ line: cursor?.line ?? 0, ch: to },
|
|
227
239
|
{ className: `${eccgui}-autosuggestion__text--highlighted` }
|
|
228
240
|
);
|
|
229
241
|
return () => marker.clear();
|
|
@@ -335,12 +347,15 @@ export const AutoSuggestion = ({
|
|
|
335
347
|
suggestionRequestData.current.requestId = requestId;
|
|
336
348
|
setSuggestionsPending(true);
|
|
337
349
|
try {
|
|
338
|
-
const
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
350
|
+
const pos = editorState.editorInstance?.getCursor();
|
|
351
|
+
if (pos) {
|
|
352
|
+
const result: IPartialAutoCompleteResult | undefined = await fetchSuggestions(
|
|
353
|
+
inputString.split("\n")[pos.line],
|
|
354
|
+
cursorPosition
|
|
355
|
+
);
|
|
356
|
+
if (value.current === inputString) {
|
|
357
|
+
setSuggestionResponse(result);
|
|
358
|
+
}
|
|
344
359
|
}
|
|
345
360
|
} catch (e) {
|
|
346
361
|
setSuggestionResponse(undefined);
|
|
@@ -377,14 +392,30 @@ export const AutoSuggestion = ({
|
|
|
377
392
|
handleEditorInputChange.cancel();
|
|
378
393
|
handleEditorInputChange(value.current, cursorPosition.current);
|
|
379
394
|
}
|
|
380
|
-
|
|
381
|
-
|
|
395
|
+
|
|
396
|
+
const boxOffsetHeight = autoSuggestionDivRef.current?.offsetHeight ?? 0;
|
|
397
|
+
|
|
398
|
+
setTimeout(() => {
|
|
399
|
+
dropdownXYoffset.current = {
|
|
400
|
+
x:
|
|
401
|
+
Math.min(coords.left, Math.max(coords.left - scrollinfo.left, 0)) +
|
|
402
|
+
(multiline ? LINE_COLUMN_WIDTH : 0),
|
|
403
|
+
y: multiline
|
|
404
|
+
? -(boxOffsetHeight - Math.min(coords.bottom, Math.max(coords.bottom - scrollinfo.top, 0))) +
|
|
405
|
+
EXTRA_VERTICAL_PADDING
|
|
406
|
+
: 0,
|
|
407
|
+
};
|
|
408
|
+
}, 1);
|
|
382
409
|
};
|
|
383
410
|
|
|
384
411
|
const handleInputEditorKeyPress = (event: KeyboardEvent) => {
|
|
385
412
|
const overWrittenKeys: Array<string> = Object.values(OVERWRITTEN_KEYS);
|
|
386
413
|
if (overWrittenKeys.includes(event.key) && (useTabForCompletions || event.key !== OVERWRITTEN_KEYS.Tab)) {
|
|
387
|
-
|
|
414
|
+
//don't prevent when enter should create new line (multiline config) and dropdown isn't shown
|
|
415
|
+
const allowDefaultEnterKeyPressBehavior = multiline && !editorState.suggestions.length;
|
|
416
|
+
if (!allowDefaultEnterKeyPressBehavior) {
|
|
417
|
+
event.preventDefault();
|
|
418
|
+
}
|
|
388
419
|
makeDropDownRespondToKeyPress(OVERWRITTEN_KEYS[event.key as keyof typeof OVERWRITTEN_KEYS]);
|
|
389
420
|
}
|
|
390
421
|
};
|
|
@@ -397,14 +428,15 @@ export const AutoSuggestion = ({
|
|
|
397
428
|
const handleDropdownChange = (selectedSuggestion: ISuggestionWithReplacementInfo) => {
|
|
398
429
|
if (selectedSuggestion && editorState.editorInstance) {
|
|
399
430
|
const { from, length, value } = selectedSuggestion;
|
|
431
|
+
const cursor = editorState.editorInstance.getCursor();
|
|
400
432
|
const to = from + length;
|
|
401
433
|
editorState.editorInstance.replaceRange(
|
|
402
434
|
selectedSuggestion.value,
|
|
403
|
-
{ line:
|
|
404
|
-
{ line:
|
|
435
|
+
{ line: cursor.line, ch: from },
|
|
436
|
+
{ line: cursor.line, ch: to }
|
|
405
437
|
);
|
|
406
438
|
closeDropDown();
|
|
407
|
-
editorState.editorInstance.setCursor({ line:
|
|
439
|
+
editorState.editorInstance.setCursor({ line: cursor.line, ch: from + value.length });
|
|
408
440
|
editorState.editorInstance.focus();
|
|
409
441
|
}
|
|
410
442
|
};
|
|
@@ -432,6 +464,18 @@ export const AutoSuggestion = ({
|
|
|
432
464
|
}
|
|
433
465
|
};
|
|
434
466
|
|
|
467
|
+
const handleInputMouseDown = React.useCallback((editor: CodeMirrorEditor) => {
|
|
468
|
+
const currentLine = editorState.editorInstance?.getCursor()?.line;
|
|
469
|
+
const clickedLine = editor.getCursor()?.line;
|
|
470
|
+
//Clicking on a different line other than the current line
|
|
471
|
+
//where the dropdown already suggests should close the dropdown
|
|
472
|
+
if (currentLine !== clickedLine) {
|
|
473
|
+
closeDropDown();
|
|
474
|
+
editorState.suggestions = [];
|
|
475
|
+
setSuggestions([]);
|
|
476
|
+
}
|
|
477
|
+
}, []);
|
|
478
|
+
|
|
435
479
|
//keyboard handlers
|
|
436
480
|
const handleArrowDown = () => {
|
|
437
481
|
const lastSuggestionIndex = editorState.suggestions.length - 1;
|
|
@@ -452,6 +496,12 @@ export const AutoSuggestion = ({
|
|
|
452
496
|
handleDropdownChange(editorState.suggestions[currentIndex()]);
|
|
453
497
|
};
|
|
454
498
|
|
|
499
|
+
const handleEscapePressed = () => {
|
|
500
|
+
closeDropDown();
|
|
501
|
+
editorState.suggestions = [];
|
|
502
|
+
setSuggestions([]);
|
|
503
|
+
};
|
|
504
|
+
|
|
455
505
|
const makeDropDownRespondToKeyPress = (keyPressedFromInput: OverwrittenKeyTypes) => {
|
|
456
506
|
// React state unknown
|
|
457
507
|
if (editorState.dropdownShown) {
|
|
@@ -468,6 +518,9 @@ export const AutoSuggestion = ({
|
|
|
468
518
|
case OVERWRITTEN_KEYS.Tab:
|
|
469
519
|
handleTabPressed();
|
|
470
520
|
break;
|
|
521
|
+
case OVERWRITTEN_KEYS.Escape:
|
|
522
|
+
handleEscapePressed();
|
|
523
|
+
break;
|
|
471
524
|
default:
|
|
472
525
|
//do nothing
|
|
473
526
|
}
|
|
@@ -485,18 +538,15 @@ export const AutoSuggestion = ({
|
|
|
485
538
|
[]
|
|
486
539
|
);
|
|
487
540
|
|
|
488
|
-
const subscribeToHorizontalShift = React.useMemo(
|
|
489
|
-
() => (callback: HorizontalShiftCallbackFunction) => {
|
|
490
|
-
horizontalShiftSubscriber.current = callback;
|
|
491
|
-
},
|
|
492
|
-
[]
|
|
493
|
-
);
|
|
494
|
-
|
|
495
541
|
const hasError = !!value.current && !pathIsValid && !pathValidationPending;
|
|
496
542
|
const autoSuggestionInput = (
|
|
497
|
-
<div
|
|
543
|
+
<div
|
|
544
|
+
id={id}
|
|
545
|
+
ref={autoSuggestionDivRef}
|
|
546
|
+
className={`${eccgui}-autosuggestion` + (className ? ` ${className}` : "")}
|
|
547
|
+
>
|
|
498
548
|
<div
|
|
499
|
-
className={
|
|
549
|
+
className={` ${eccgui}-autosuggestion__inputfield ${BlueprintClassNames.INPUT_GROUP} ${
|
|
500
550
|
BlueprintClassNames.FILL
|
|
501
551
|
} ${hasError ? BlueprintClassNames.INTENT_DANGER : ""}`}
|
|
502
552
|
>
|
|
@@ -505,12 +555,13 @@ export const AutoSuggestion = ({
|
|
|
505
555
|
fill
|
|
506
556
|
isOpen={shouldShowDropdown}
|
|
507
557
|
placement="bottom-start"
|
|
558
|
+
modifiers={{ flip: { enabled: false } }}
|
|
508
559
|
openOnTargetFocus={false}
|
|
509
560
|
autoFocus={false}
|
|
510
561
|
content={
|
|
511
562
|
<AutoSuggestionList
|
|
512
563
|
id={id + "__dropdown"}
|
|
513
|
-
|
|
564
|
+
offsetValues={dropdownXYoffset.current}
|
|
514
565
|
loading={suggestionsPending}
|
|
515
566
|
options={suggestions}
|
|
516
567
|
isOpen={!suggestionsPending && shouldShowDropdown}
|
|
@@ -520,8 +571,8 @@ export const AutoSuggestion = ({
|
|
|
520
571
|
/>
|
|
521
572
|
}
|
|
522
573
|
>
|
|
523
|
-
<
|
|
524
|
-
mode=
|
|
574
|
+
<ExtendedCodeEditor
|
|
575
|
+
mode={mode}
|
|
525
576
|
setEditorInstance={setEditorInstance}
|
|
526
577
|
onChange={handleChange}
|
|
527
578
|
onCursorChange={handleCursorChange}
|
|
@@ -532,6 +583,8 @@ export const AutoSuggestion = ({
|
|
|
532
583
|
placeholder={placeholder}
|
|
533
584
|
onSelection={onSelection}
|
|
534
585
|
showScrollBar={showScrollBar}
|
|
586
|
+
multiline={multiline}
|
|
587
|
+
onMouseDown={handleInputMouseDown}
|
|
535
588
|
/>
|
|
536
589
|
</ContextOverlay>
|
|
537
590
|
{!!value.current && (
|
|
@@ -24,38 +24,30 @@ export interface AutoSuggestionListProps extends Omit<React.HTMLAttributes<HTMLD
|
|
|
24
24
|
isOpen: boolean;
|
|
25
25
|
// If the drop down should show a loading state
|
|
26
26
|
loading?: boolean;
|
|
27
|
-
// Register for changes in horizontal shift
|
|
28
|
-
registerForHorizontalShift?: (callback: HorizontalShiftCallbackFunction) => any
|
|
29
27
|
// The item from the drop down that is active
|
|
30
28
|
currentlyFocusedIndex: number;
|
|
31
29
|
// Callback indicating what item should currently being highlighted, i.e. is either active or is hovered over
|
|
32
30
|
itemToHighlight: (item: ISuggestionWithReplacementInfo | undefined) => any;
|
|
31
|
+
/** horizontal and vertical offset values in relation to the cursor */
|
|
32
|
+
offsetValues?: { x: number; y: number };
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
// @deprecated
|
|
36
36
|
export type IDropdownProps = AutoSuggestionListProps;
|
|
37
37
|
|
|
38
|
-
type HorizontalShiftCallbackFunction = (shift: number) => any
|
|
39
|
-
|
|
40
38
|
const ListItem = ({ item }: any, ref: any) => {
|
|
41
39
|
const listItem = (
|
|
42
40
|
<OverviewItem densityHigh={true}>
|
|
43
41
|
<OverviewItemDescription>
|
|
44
42
|
<OverviewItemLine>
|
|
45
43
|
<OverflowText ellipsis="reverse">
|
|
46
|
-
<Highlighter
|
|
47
|
-
label={item.value}
|
|
48
|
-
searchValue={item.query}
|
|
49
|
-
/>
|
|
44
|
+
<Highlighter label={item.value} searchValue={item.query} />
|
|
50
45
|
</OverflowText>
|
|
51
46
|
</OverviewItemLine>
|
|
52
47
|
{item.description ? (
|
|
53
48
|
<OverviewItemLine small={true}>
|
|
54
49
|
<OverflowText>
|
|
55
|
-
<Highlighter
|
|
56
|
-
label={item.description}
|
|
57
|
-
searchValue={item.query}
|
|
58
|
-
/>
|
|
50
|
+
<Highlighter label={item.description} searchValue={item.query} />
|
|
59
51
|
</OverflowText>
|
|
60
52
|
</OverviewItemLine>
|
|
61
53
|
) : null}
|
|
@@ -82,18 +74,15 @@ export const AutoSuggestionList = ({
|
|
|
82
74
|
options,
|
|
83
75
|
loading,
|
|
84
76
|
onItemSelectionChange,
|
|
85
|
-
registerForHorizontalShift,
|
|
86
77
|
currentlyFocusedIndex,
|
|
87
78
|
itemToHighlight,
|
|
88
79
|
style,
|
|
80
|
+
offsetValues,
|
|
89
81
|
...otherDivProps
|
|
90
82
|
}: AutoSuggestionListProps) => {
|
|
91
|
-
const [hoveredItem, setHoveredItem] = React.useState<
|
|
92
|
-
ISuggestionWithReplacementInfo | undefined
|
|
93
|
-
>(undefined);
|
|
94
|
-
const [left, setLeft] = React.useState(0)
|
|
83
|
+
const [hoveredItem, setHoveredItem] = React.useState<ISuggestionWithReplacementInfo | undefined>(undefined);
|
|
95
84
|
// Refs of list items
|
|
96
|
-
const [refs] = React.useState<React.RefObject<Element>[]>([])
|
|
85
|
+
const [refs] = React.useState<React.RefObject<Element>[]>([]);
|
|
97
86
|
const dropdownRef = React.useRef<HTMLDivElement>(null);
|
|
98
87
|
const generateRef = (index: number) => {
|
|
99
88
|
if (!refs[index]) {
|
|
@@ -102,15 +91,6 @@ export const AutoSuggestionList = ({
|
|
|
102
91
|
return refs[index];
|
|
103
92
|
};
|
|
104
93
|
|
|
105
|
-
React.useEffect(() => {
|
|
106
|
-
if(registerForHorizontalShift) {
|
|
107
|
-
const callback = (shift: number) => {
|
|
108
|
-
setTimeout(() => setLeft(shift), 1)
|
|
109
|
-
}
|
|
110
|
-
registerForHorizontalShift(callback)
|
|
111
|
-
}
|
|
112
|
-
}, [registerForHorizontalShift])
|
|
113
|
-
|
|
114
94
|
React.useEffect(() => {
|
|
115
95
|
const listIndexNode = refs[currentlyFocusedIndex];
|
|
116
96
|
if (dropdownRef?.current && listIndexNode?.current) {
|
|
@@ -126,18 +106,12 @@ export const AutoSuggestionList = ({
|
|
|
126
106
|
}
|
|
127
107
|
}, [currentlyFocusedIndex, refs]);
|
|
128
108
|
|
|
129
|
-
const focusedItem = options[currentlyFocusedIndex]
|
|
109
|
+
const focusedItem = options[currentlyFocusedIndex];
|
|
130
110
|
|
|
131
111
|
// Decide which item to highlight
|
|
132
112
|
React.useEffect(() => {
|
|
133
113
|
itemToHighlight(!isOpen ? undefined : hoveredItem || focusedItem);
|
|
134
|
-
}, [
|
|
135
|
-
currentlyFocusedIndex,
|
|
136
|
-
itemToHighlight,
|
|
137
|
-
focusedItem,
|
|
138
|
-
isOpen,
|
|
139
|
-
hoveredItem,
|
|
140
|
-
]);
|
|
114
|
+
}, [currentlyFocusedIndex, itemToHighlight, focusedItem, isOpen, hoveredItem]);
|
|
141
115
|
|
|
142
116
|
const Loader = (
|
|
143
117
|
<OverviewItem hasSpacing>
|
|
@@ -153,7 +127,7 @@ export const AutoSuggestionList = ({
|
|
|
153
127
|
<div
|
|
154
128
|
{...otherDivProps}
|
|
155
129
|
className={`${eccgui}-autosuggestion__dropdown`}
|
|
156
|
-
style={{ ...style, left }}
|
|
130
|
+
style={{ ...style, left: offsetValues?.x ?? 0, top: offsetValues?.y ?? 0 }}
|
|
157
131
|
ref={dropdownRef}
|
|
158
132
|
>
|
|
159
133
|
{loading ? (
|
|
@@ -166,12 +140,7 @@ export const AutoSuggestionList = ({
|
|
|
166
140
|
active={currentlyFocusedIndex === index}
|
|
167
141
|
onMouseDown={(e: any) => e.preventDefault()}
|
|
168
142
|
onClick={() => onItemSelectionChange(item)}
|
|
169
|
-
text={(
|
|
170
|
-
<Item
|
|
171
|
-
ref={generateRef(index)}
|
|
172
|
-
item={item}
|
|
173
|
-
/>
|
|
174
|
-
)}
|
|
143
|
+
text={<Item ref={generateRef(index)} item={item} />}
|
|
175
144
|
onMouseEnter={() => setHoveredItem(item)}
|
|
176
145
|
onMouseLeave={() => setHoveredItem(undefined)}
|
|
177
146
|
onMouseOver={() => {
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import "codemirror/addon/display/placeholder.js";
|
|
2
|
+
import "codemirror/mode/sparql/sparql.js";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { UnControlled as UnControlledEditor } from "react-codemirror2";
|
|
5
|
+
import { Classes as BlueprintClassNames } from "@blueprintjs/core";
|
|
6
|
+
import { Editor as CodeMirrorEditor, EditorChange } from "codemirror";
|
|
7
|
+
import { CLASSPREFIX as eccgui } from "../../configuration/constants";
|
|
8
|
+
|
|
9
|
+
export interface IRange {
|
|
10
|
+
from: number;
|
|
11
|
+
to: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ExtendedCodeEditorProps {
|
|
15
|
+
// Is called with the editor instance that allows access via the CodeMirror API
|
|
16
|
+
setEditorInstance: (editor: CodeMirrorEditor) => any;
|
|
17
|
+
// Called whenever the editor content changes
|
|
18
|
+
onChange: (value: string) => any;
|
|
19
|
+
// Called when the cursor position changes
|
|
20
|
+
onCursorChange: (pos: any, coords: any, scrollinfo: any) => any;
|
|
21
|
+
// The editor theme, e.g. "sparql"
|
|
22
|
+
mode?: string;
|
|
23
|
+
// The initial value of the editor
|
|
24
|
+
initialValue: string;
|
|
25
|
+
// Called when the focus status changes
|
|
26
|
+
onFocusChange: (focused: boolean) => any;
|
|
27
|
+
// Called when the user presses a key
|
|
28
|
+
onKeyDown: (event: KeyboardEvent) => any;
|
|
29
|
+
// function invoked when any click occurs
|
|
30
|
+
onMouseDown?: (editor: CodeMirrorEditor) => any;
|
|
31
|
+
// Called when the user selects text
|
|
32
|
+
onSelection: (ranges: IRange[]) => any;
|
|
33
|
+
// If the <Tab> key is enabled as normal input, i.e. it won't have the behavior of changing to the next input element, expected in a web app.
|
|
34
|
+
enableTab?: boolean;
|
|
35
|
+
/** Placeholder tobe shown when no text has been entered, yet. */
|
|
36
|
+
placeholder?: string;
|
|
37
|
+
//show scrollbar
|
|
38
|
+
showScrollBar?: boolean;
|
|
39
|
+
/** allow multiline entries when new line characters are entered */
|
|
40
|
+
multiline?: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type IEditorProps = ExtendedCodeEditorProps;
|
|
44
|
+
|
|
45
|
+
/** A single-line code editor. */
|
|
46
|
+
export const ExtendedCodeEditor = ({
|
|
47
|
+
setEditorInstance,
|
|
48
|
+
onChange,
|
|
49
|
+
onCursorChange,
|
|
50
|
+
mode,
|
|
51
|
+
initialValue = "",
|
|
52
|
+
onFocusChange,
|
|
53
|
+
onKeyDown,
|
|
54
|
+
onSelection,
|
|
55
|
+
enableTab = false,
|
|
56
|
+
placeholder,
|
|
57
|
+
showScrollBar = true,
|
|
58
|
+
multiline = false,
|
|
59
|
+
onMouseDown,
|
|
60
|
+
}: ExtendedCodeEditorProps) => {
|
|
61
|
+
const initialContent = React.useRef(multiline ? initialValue : initialValue.replace(/[\r\n]/g, " "));
|
|
62
|
+
|
|
63
|
+
const extendedEditorProps = {
|
|
64
|
+
editorDidMount: (editor: any) => {
|
|
65
|
+
editor.on("beforeChange", (_: any, change: any) => {
|
|
66
|
+
// Prevent the user from entering new-line characters, since this is supposed to be a one-line editor.
|
|
67
|
+
if (change.update && typeof change.update === "function" && change.text.length > 1) {
|
|
68
|
+
change.update(change.from, change.to, [change.text.join("")]);
|
|
69
|
+
}
|
|
70
|
+
return true;
|
|
71
|
+
});
|
|
72
|
+
setEditorInstance(editor);
|
|
73
|
+
},
|
|
74
|
+
onBeforeChange: (_editor: CodeMirrorEditor, data: EditorChange, _: string, next: () => any) => {
|
|
75
|
+
// Reduce multiple lines to a single line
|
|
76
|
+
if (data.text.length > 1) {
|
|
77
|
+
_editor.setValue(data.text.join(""));
|
|
78
|
+
}
|
|
79
|
+
next();
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const extraEditorProps = multiline
|
|
84
|
+
? {
|
|
85
|
+
editorDidMount: (editor: any) => {
|
|
86
|
+
setEditorInstance(editor);
|
|
87
|
+
},
|
|
88
|
+
}
|
|
89
|
+
: extendedEditorProps;
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<div className={`${eccgui}-${multiline ? "codeeditor" : `singlelinecodeeditor ${BlueprintClassNames.INPUT}`}`}>
|
|
93
|
+
<UnControlledEditor
|
|
94
|
+
value={initialContent.current}
|
|
95
|
+
onFocus={() => onFocusChange(true)}
|
|
96
|
+
onBlur={() => onFocusChange(false)}
|
|
97
|
+
options={{
|
|
98
|
+
mode: mode,
|
|
99
|
+
lineNumbers: multiline,
|
|
100
|
+
lineWrapping: multiline,
|
|
101
|
+
theme: "xq-light",
|
|
102
|
+
extraKeys: enableTab ? undefined : { Tab: false },
|
|
103
|
+
placeholder,
|
|
104
|
+
scrollbarStyle: showScrollBar ? "native" : "null",
|
|
105
|
+
}}
|
|
106
|
+
onSelection={(_editor, data) => {
|
|
107
|
+
if (Array.isArray(data?.ranges)) {
|
|
108
|
+
onSelection(
|
|
109
|
+
data.ranges
|
|
110
|
+
.map((r: any) => ({ from: r.from().ch, to: r.to().ch }))
|
|
111
|
+
.filter((r: any) => r.from !== r.to)
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
}}
|
|
115
|
+
onCursor={(editor, data) => {
|
|
116
|
+
onCursorChange(data, editor.cursorCoords(true, "local"), editor.getScrollInfo());
|
|
117
|
+
}}
|
|
118
|
+
onChange={(_editor, _data, value) => {
|
|
119
|
+
onChange(value);
|
|
120
|
+
}}
|
|
121
|
+
onMouseDown={(editor) => onMouseDown && onMouseDown(editor)}
|
|
122
|
+
onKeyDown={(_, event) => onKeyDown(event)}
|
|
123
|
+
{...extraEditorProps}
|
|
124
|
+
/>
|
|
125
|
+
</div>
|
|
126
|
+
);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export default ExtendedCodeEditor;
|
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
import { AutoSuggestion, AutoSuggestionProps } from "./AutoSuggestion";
|
|
2
2
|
import { AutoSuggestionList, AutoSuggestionListProps } from "./AutoSuggestionList";
|
|
3
|
-
import {
|
|
3
|
+
import { ExtendedCodeEditor, ExtendedCodeEditorProps } from "./ExtendedCodeEditor";
|
|
4
4
|
|
|
5
|
-
export {
|
|
6
|
-
AutoSuggestion,
|
|
7
|
-
AutoSuggestionList,
|
|
8
|
-
SingleLineCodeEditor,
|
|
9
|
-
};
|
|
5
|
+
export { AutoSuggestion, AutoSuggestionList, ExtendedCodeEditor };
|
|
10
6
|
|
|
11
|
-
export type {
|
|
12
|
-
AutoSuggestionProps,
|
|
13
|
-
AutoSuggestionListProps,
|
|
14
|
-
SingleLineCodeEditorProps,
|
|
15
|
-
};
|
|
7
|
+
export type { AutoSuggestionProps, AutoSuggestionListProps, ExtendedCodeEditorProps };
|