@eccenca/gui-elements 23.2.0-rc.0 → 23.2.0-rc.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/CHANGELOG.md +28 -3
- package/dist/cjs/components/AutoSuggestion/AutoSuggestion.js +24 -20
- package/dist/cjs/components/AutoSuggestion/AutoSuggestion.js.map +1 -1
- package/dist/cjs/components/AutocompleteField/AutoCompleteField.js +59 -50
- package/dist/cjs/components/AutocompleteField/AutoCompleteField.js.map +1 -1
- package/dist/cjs/components/AutocompleteField/autoCompleteFieldUtils.js +3 -3
- package/dist/cjs/components/AutocompleteField/autoCompleteFieldUtils.js.map +1 -1
- package/dist/cjs/components/Breadcrumb/BreadcrumbItem.js +9 -6
- package/dist/cjs/components/Breadcrumb/BreadcrumbItem.js.map +1 -1
- package/dist/cjs/components/CodeAutocompleteField/CodeAutocompleteField.js +33 -0
- package/dist/cjs/components/CodeAutocompleteField/CodeAutocompleteField.js.map +1 -0
- package/dist/cjs/components/CodeAutocompleteField/index.js +14 -0
- package/dist/cjs/components/CodeAutocompleteField/index.js.map +1 -0
- package/dist/cjs/components/Depiction/Depiction.js +13 -13
- package/dist/cjs/components/Depiction/Depiction.js.map +1 -1
- package/dist/cjs/components/Icon/IconButton.js +9 -3
- package/dist/cjs/components/Icon/IconButton.js.map +1 -1
- package/dist/cjs/components/Icon/canonicalIconNames.js +2 -0
- package/dist/cjs/components/Icon/canonicalIconNames.js.map +1 -1
- package/dist/cjs/components/MultiSelect/MultiSelect.js +45 -17
- package/dist/cjs/components/MultiSelect/MultiSelect.js.map +1 -1
- package/dist/cjs/components/MultiSuggestField/MultiSuggestField.js +33 -0
- package/dist/cjs/components/MultiSuggestField/MultiSuggestField.js.map +1 -0
- package/dist/cjs/components/OverviewItem/OverviewItem.js +8 -10
- package/dist/cjs/components/OverviewItem/OverviewItem.js.map +1 -1
- package/dist/cjs/components/PropertyValuePair/PropertyName.js +4 -3
- package/dist/cjs/components/PropertyValuePair/PropertyName.js.map +1 -1
- package/dist/cjs/components/Select/Select.js +17 -3
- package/dist/cjs/components/Select/Select.js.map +1 -1
- package/dist/cjs/components/SuggestField/SuggestField.js +39 -0
- package/dist/cjs/components/SuggestField/SuggestField.js.map +1 -0
- package/dist/cjs/components/SuggestField/index.js +19 -0
- package/dist/cjs/components/SuggestField/index.js.map +1 -0
- package/dist/cjs/components/index.js +3 -0
- package/dist/cjs/components/index.js.map +1 -1
- package/dist/cjs/extensions/react-flow/nodes/NodeContent.js +54 -41
- package/dist/cjs/extensions/react-flow/nodes/NodeContent.js.map +1 -1
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/components/AutoSuggestion/AutoSuggestion.js +23 -18
- package/dist/esm/components/AutoSuggestion/AutoSuggestion.js.map +1 -1
- package/dist/esm/components/AutocompleteField/AutoCompleteField.js +68 -59
- package/dist/esm/components/AutocompleteField/AutoCompleteField.js.map +1 -1
- package/dist/esm/components/AutocompleteField/autoCompleteFieldUtils.js +3 -3
- package/dist/esm/components/AutocompleteField/autoCompleteFieldUtils.js.map +1 -1
- package/dist/esm/components/Breadcrumb/BreadcrumbItem.js +9 -6
- package/dist/esm/components/Breadcrumb/BreadcrumbItem.js.map +1 -1
- package/dist/esm/components/CodeAutocompleteField/CodeAutocompleteField.js +37 -0
- package/dist/esm/components/CodeAutocompleteField/CodeAutocompleteField.js.map +1 -0
- package/dist/esm/components/CodeAutocompleteField/index.js +2 -0
- package/dist/esm/components/CodeAutocompleteField/index.js.map +1 -0
- package/dist/esm/components/Depiction/Depiction.js +12 -12
- package/dist/esm/components/Depiction/Depiction.js.map +1 -1
- package/dist/esm/components/Icon/IconButton.js +10 -4
- package/dist/esm/components/Icon/IconButton.js.map +1 -1
- package/dist/esm/components/Icon/canonicalIconNames.js +2 -0
- package/dist/esm/components/Icon/canonicalIconNames.js.map +1 -1
- package/dist/esm/components/MultiSelect/MultiSelect.js +52 -22
- package/dist/esm/components/MultiSelect/MultiSelect.js.map +1 -1
- package/dist/esm/components/MultiSuggestField/MultiSuggestField.js +37 -0
- package/dist/esm/components/MultiSuggestField/MultiSuggestField.js.map +1 -0
- package/dist/esm/components/OverviewItem/OverviewItem.js +8 -10
- package/dist/esm/components/OverviewItem/OverviewItem.js.map +1 -1
- package/dist/esm/components/PropertyValuePair/PropertyName.js +4 -3
- package/dist/esm/components/PropertyValuePair/PropertyName.js.map +1 -1
- package/dist/esm/components/Select/Select.js +18 -4
- package/dist/esm/components/Select/Select.js.map +1 -1
- package/dist/esm/components/SuggestField/SuggestField.js +43 -0
- package/dist/esm/components/SuggestField/SuggestField.js.map +1 -0
- package/dist/esm/components/SuggestField/index.js +6 -0
- package/dist/esm/components/SuggestField/index.js.map +1 -0
- package/dist/esm/components/index.js +3 -0
- package/dist/esm/components/index.js.map +1 -1
- package/dist/esm/extensions/react-flow/nodes/NodeContent.js +51 -39
- package/dist/esm/extensions/react-flow/nodes/NodeContent.js.map +1 -1
- package/dist/esm/index.js +2 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/types/components/AutoSuggestion/AutoSuggestion.d.ts +35 -4
- package/dist/types/components/AutocompleteField/AutoCompleteField.d.ts +23 -6
- package/dist/types/components/AutocompleteField/autoCompleteFieldUtils.d.ts +3 -3
- package/dist/types/components/AutocompleteField/index.d.ts +1 -1
- package/dist/types/components/AutocompleteField/interfaces.d.ts +1 -1
- package/dist/types/components/CodeAutocompleteField/CodeAutocompleteField.d.ts +10 -0
- package/dist/types/components/CodeAutocompleteField/index.d.ts +1 -0
- package/dist/types/components/Icon/IconButton.d.ts +2 -2
- package/dist/types/components/Icon/canonicalIconNames.d.ts +1 -1
- package/dist/types/components/MultiSelect/MultiSelect.d.ts +20 -2
- package/dist/types/components/MultiSuggestField/MultiSuggestField.d.ts +10 -0
- package/dist/types/components/PropertyValuePair/PropertyName.d.ts +5 -1
- package/dist/types/components/Select/Select.d.ts +28 -9
- package/dist/types/components/SuggestField/SuggestField.d.ts +16 -0
- package/dist/types/components/SuggestField/index.d.ts +5 -0
- package/dist/types/components/index.d.ts +3 -0
- package/dist/types/extensions/react-flow/nodes/NodeContent.d.ts +7 -7
- package/dist/types/index.d.ts +2 -1
- package/package.json +1 -1
- package/src/components/AutoSuggestion/AutoSuggestion.stories.tsx +14 -0
- package/src/components/AutoSuggestion/AutoSuggestion.tsx +280 -221
- package/src/components/AutocompleteField/AutoCompleteField.stories.tsx +14 -0
- package/src/components/AutocompleteField/AutoCompleteField.tsx +157 -121
- package/src/components/AutocompleteField/autoCompleteFieldUtils.tsx +10 -16
- package/src/components/AutocompleteField/interfaces.ts +5 -6
- package/src/components/Breadcrumb/BreadcrumbItem.tsx +19 -18
- package/src/components/Breadcrumb/breadcrumb.scss +18 -16
- package/src/components/Breadcrumb/stories/BreadcrumbList.stories.tsx +18 -15
- package/src/components/CodeAutocompleteField/CodeAutocompleteField.stories.tsx +89 -0
- package/src/components/CodeAutocompleteField/CodeAutocompleteField.tsx +22 -0
- package/src/components/CodeAutocompleteField/index.ts +1 -0
- package/src/components/ContextOverlay/contextoverlay.scss +10 -2
- package/src/components/Depiction/Depiction.tsx +57 -55
- package/src/components/Icon/IconButton.tsx +25 -11
- package/src/components/Icon/canonicalIconNames.tsx +4 -0
- package/src/components/MultiSelect/MultiSelect.stories.tsx +9 -47
- package/src/components/MultiSelect/MultiSelect.tsx +110 -66
- package/src/components/MultiSuggestField/MultiSuggestField.stories.tsx +58 -0
- package/src/components/MultiSuggestField/MultiSuggestField.tsx +22 -0
- package/src/components/Notification/notification.scss +20 -17
- package/src/components/OverviewItem/OverviewItem.tsx +11 -20
- package/src/components/PropertyValuePair/PropertyName.tsx +17 -4
- package/src/components/PropertyValuePair/propertyvalue.scss +17 -1
- package/src/components/Select/Select.stories.tsx +49 -20
- package/src/components/Select/Select.tsx +93 -18
- package/src/components/Select/_select.scss +13 -1
- package/src/components/SuggestField/SuggestField.stories.tsx +99 -0
- package/src/components/SuggestField/SuggestField.tsx +28 -0
- package/src/components/SuggestField/index.ts +5 -0
- package/src/components/TagInput/taginput.scss +14 -0
- package/src/components/index.scss +1 -1
- package/src/components/index.ts +3 -0
- package/src/extensions/react-flow/nodes/NodeContent.tsx +105 -87
- package/src/index.ts +2 -5
- package/src/components/AutoSuggestion/stories/AutoSuggestion.stories.tsx +0 -83
- package/src/components/AutocompleteField/stories/AutoCompleteField.stories.tsx +0 -86
|
@@ -1,18 +1,14 @@
|
|
|
1
|
-
import React, {useEffect, useMemo, useState} from "react";
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
1
|
+
import React, { useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { Classes as BlueprintClassNames } from "@blueprintjs/core";
|
|
3
|
+
import CodeMirror, { Position } from "codemirror";
|
|
4
|
+
import { debounce } from "lodash";
|
|
5
|
+
|
|
5
6
|
import { CLASSPREFIX as eccgui } from "../../configuration/constants";
|
|
6
7
|
|
|
8
|
+
import { ContextOverlay, FieldItem, IconButton, Spinner, Toolbar, ToolbarSection } from "./../../";
|
|
9
|
+
import { AutoSuggestionList } from "./AutoSuggestionList";
|
|
7
10
|
//custom components
|
|
8
|
-
import SingleLineCodeEditor, {IRange} from "./SingleLineCodeEditor";
|
|
9
|
-
import {AutoSuggestionList} from "./AutoSuggestionList";
|
|
10
|
-
import {debounce} from "lodash";
|
|
11
|
-
|
|
12
|
-
/*
|
|
13
|
-
FIXME: we should find a better name for this component.
|
|
14
|
-
`AutoSuggestion` sounds not exact enough and difference to `AutoCompleteField` is not clear enough.
|
|
15
|
-
*/
|
|
11
|
+
import SingleLineCodeEditor, { IRange } from "./SingleLineCodeEditor";
|
|
16
12
|
|
|
17
13
|
export enum OVERWRITTEN_KEYS {
|
|
18
14
|
ArrowUp = "ArrowUp",
|
|
@@ -20,100 +16,118 @@ export enum OVERWRITTEN_KEYS {
|
|
|
20
16
|
Enter = "Enter",
|
|
21
17
|
Tab = "Tab",
|
|
22
18
|
}
|
|
23
|
-
export type OverwrittenKeyTypes = typeof OVERWRITTEN_KEYS[keyof typeof OVERWRITTEN_KEYS];
|
|
19
|
+
export type OverwrittenKeyTypes = (typeof OVERWRITTEN_KEYS)[keyof typeof OVERWRITTEN_KEYS];
|
|
24
20
|
|
|
25
21
|
/** A single suggestion. */
|
|
26
22
|
export interface ISuggestionBase {
|
|
27
23
|
// The actual value
|
|
28
|
-
value: string
|
|
24
|
+
value: string;
|
|
29
25
|
// Optional human-readable label
|
|
30
|
-
label?: string
|
|
26
|
+
label?: string;
|
|
31
27
|
// Optional description of the value.
|
|
32
|
-
description?: string
|
|
28
|
+
description?: string;
|
|
33
29
|
}
|
|
34
30
|
|
|
35
31
|
/** Same as ISuggestionBase, but with the query that was used to fetch this suggestion. */
|
|
36
32
|
export interface ISuggestionWithReplacementInfo extends ISuggestionBase {
|
|
37
33
|
// The query this result was filtered by
|
|
38
|
-
query: string
|
|
34
|
+
query: string;
|
|
39
35
|
// The offset of the original string that should be replaced
|
|
40
|
-
from: number
|
|
36
|
+
from: number;
|
|
41
37
|
// The length of the original string that should be replaced
|
|
42
|
-
length: number
|
|
38
|
+
length: number;
|
|
43
39
|
}
|
|
44
40
|
|
|
45
41
|
/** The suggestions for a specific substring of the given input string. */
|
|
46
42
|
export interface IReplacementResult {
|
|
47
43
|
// The range of the input string that should be replaced
|
|
48
44
|
replacementInterval: {
|
|
49
|
-
from: number
|
|
50
|
-
length: number
|
|
51
|
-
}
|
|
45
|
+
from: number;
|
|
46
|
+
length: number;
|
|
47
|
+
};
|
|
52
48
|
// The extracted query from the local value at the cursor position of the path that was used to retrieve the suggestions.
|
|
53
|
-
extractedQuery: string
|
|
49
|
+
extractedQuery: string;
|
|
54
50
|
// The suggested replacements for the substring that should be replaced.
|
|
55
|
-
replacements: ISuggestionBase[]
|
|
51
|
+
replacements: ISuggestionBase[];
|
|
56
52
|
}
|
|
57
53
|
|
|
58
54
|
export interface IPartialAutoCompleteResult {
|
|
59
55
|
// Repeats the input string from the corresponding request
|
|
60
|
-
inputString: string
|
|
56
|
+
inputString: string;
|
|
61
57
|
// Repeats the cursor position from the corresponding request
|
|
62
|
-
cursorPosition: number
|
|
63
|
-
replacementResults: IReplacementResult[]
|
|
58
|
+
cursorPosition: number;
|
|
59
|
+
replacementResults: IReplacementResult[];
|
|
64
60
|
}
|
|
65
61
|
|
|
66
62
|
/** Validation result */
|
|
67
63
|
export interface IValidationResult {
|
|
68
64
|
// If the input value is valid or not
|
|
69
|
-
valid: boolean
|
|
65
|
+
valid: boolean;
|
|
70
66
|
parseError?: {
|
|
71
67
|
// Detail error message
|
|
72
|
-
message: string
|
|
68
|
+
message: string;
|
|
73
69
|
// Start of the parse error in the input string
|
|
74
|
-
start: number
|
|
70
|
+
start: number;
|
|
75
71
|
// End of the parse error in the input string
|
|
76
|
-
end: number
|
|
77
|
-
}
|
|
72
|
+
end: number;
|
|
73
|
+
};
|
|
78
74
|
}
|
|
79
75
|
|
|
80
76
|
export interface AutoSuggestionProps {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
77
|
+
/** Additional class name.
|
|
78
|
+
*/
|
|
79
|
+
className?: string;
|
|
80
|
+
/** Optional label to be shown for the input (above). This will create a FieldItem around the input.
|
|
81
|
+
*/
|
|
82
|
+
label?: string;
|
|
83
|
+
/** The value the component is initialized with, do not use this to control value changes.
|
|
84
|
+
*/
|
|
85
|
+
initialValue: string;
|
|
86
|
+
/** Callback on value change
|
|
87
|
+
*/
|
|
88
|
+
onChange: (currentValue: string) => any;
|
|
89
|
+
/** Fetches the suggestions
|
|
90
|
+
*/
|
|
91
|
+
fetchSuggestions: (
|
|
92
|
+
inputString: string,
|
|
93
|
+
cursorPosition: number
|
|
94
|
+
) => (IPartialAutoCompleteResult | undefined) | Promise<IPartialAutoCompleteResult | undefined>;
|
|
95
|
+
/** Checks if the input is valid
|
|
96
|
+
*/
|
|
97
|
+
checkInput?: (inputString: string) => IValidationResult | Promise<IValidationResult | undefined>;
|
|
98
|
+
/** Called with the input validation result
|
|
99
|
+
*/
|
|
100
|
+
onInputChecked?: (validInput: boolean) => any;
|
|
101
|
+
/** Text that should be shown if the input validation failed.
|
|
102
|
+
*/
|
|
103
|
+
validationErrorText?: string;
|
|
104
|
+
/** Text that should be shown when hovering over the clear icon
|
|
105
|
+
*/
|
|
106
|
+
clearIconText?: string;
|
|
107
|
+
/** Called when focus status changes
|
|
108
|
+
*/
|
|
109
|
+
onFocusChange?: (hasFocus: boolean) => any;
|
|
110
|
+
/** Optional ID to attach to the outer element
|
|
111
|
+
*/
|
|
112
|
+
id?: string;
|
|
113
|
+
/** If the <Tab> key should be used for auto-completing items. Else it will have its default behavior.
|
|
114
|
+
*/
|
|
115
|
+
useTabForCompletions?: boolean;
|
|
103
116
|
/** An additional element that is put to the left side of the input field */
|
|
104
|
-
leftElement?: JSX.Element | null
|
|
105
|
-
|
|
106
|
-
|
|
117
|
+
leftElement?: JSX.Element | null;
|
|
118
|
+
/** An additional element that is put to the right side of the input field
|
|
119
|
+
*/
|
|
120
|
+
rightElement?: JSX.Element | null;
|
|
107
121
|
/** Placeholder tobe shown when no text has been entered, yet. */
|
|
108
|
-
placeholder?: string
|
|
122
|
+
placeholder?: string;
|
|
109
123
|
/** If the horizontal scrollbars should be shown. */
|
|
110
|
-
showScrollBar?: boolean
|
|
124
|
+
showScrollBar?: boolean;
|
|
111
125
|
/** Delay in ms before an auto-completion request should be send after nothing is typed in anymore.
|
|
112
126
|
* This should prevent the UI to send too many requests to the backend. */
|
|
113
|
-
autoCompletionRequestDelay?: number
|
|
127
|
+
autoCompletionRequestDelay?: number;
|
|
114
128
|
/** Delay in ms before a validation request should be send after nothing is typed in anymore.
|
|
115
129
|
* This should prevent the UI to send too many requests to the backend. */
|
|
116
|
-
validationRequestDelay?: number
|
|
130
|
+
validationRequestDelay?: number;
|
|
117
131
|
}
|
|
118
132
|
|
|
119
133
|
// @deprecated
|
|
@@ -121,122 +135,127 @@ export type IProps = AutoSuggestionProps;
|
|
|
121
135
|
|
|
122
136
|
// Meta data regarding a request
|
|
123
137
|
interface RequestMetaData {
|
|
124
|
-
requestId: string | undefined
|
|
138
|
+
requestId: string | undefined;
|
|
125
139
|
}
|
|
126
140
|
|
|
127
|
-
type HorizontalShiftCallbackFunction = (shift: number) => any
|
|
141
|
+
type HorizontalShiftCallbackFunction = (shift: number) => any;
|
|
128
142
|
|
|
129
|
-
/**
|
|
130
|
-
*
|
|
143
|
+
/**
|
|
144
|
+
* **Element is deprecated.**
|
|
145
|
+
* Use `CodeAutocompleteField` as replacement.
|
|
146
|
+
*
|
|
147
|
+
* @deprecated
|
|
148
|
+
*/
|
|
131
149
|
export const AutoSuggestion = ({
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
150
|
+
className,
|
|
151
|
+
label,
|
|
152
|
+
initialValue,
|
|
153
|
+
onChange,
|
|
154
|
+
fetchSuggestions,
|
|
155
|
+
checkInput,
|
|
156
|
+
validationErrorText = "Invalid value",
|
|
157
|
+
clearIconText = "Clear",
|
|
158
|
+
onFocusChange,
|
|
159
|
+
id,
|
|
160
|
+
onInputChecked,
|
|
161
|
+
leftElement,
|
|
162
|
+
rightElement,
|
|
163
|
+
useTabForCompletions = false,
|
|
164
|
+
placeholder,
|
|
165
|
+
showScrollBar = true,
|
|
166
|
+
autoCompletionRequestDelay = 1000,
|
|
167
|
+
validationRequestDelay = 200,
|
|
168
|
+
}: AutoSuggestionProps) => {
|
|
169
|
+
const value = React.useRef<string>(initialValue);
|
|
151
170
|
const cursorPosition = React.useRef(0);
|
|
152
|
-
const horizontalShiftSubscriber = React.useRef<HorizontalShiftCallbackFunction | undefined>(undefined)
|
|
171
|
+
const horizontalShiftSubscriber = React.useRef<HorizontalShiftCallbackFunction | undefined>(undefined);
|
|
153
172
|
const [shouldShowDropdown, setShouldShowDropdown] = React.useState(false);
|
|
154
173
|
const [suggestions, setSuggestions] = React.useState<ISuggestionWithReplacementInfo[]>([]);
|
|
155
174
|
const [suggestionsPending, setSuggestionsPending] = React.useState(false);
|
|
156
|
-
const suggestionRequestData = React.useRef<RequestMetaData>({requestId: undefined})
|
|
157
|
-
const [pathValidationPending, setPathValidationPending] = React.useState(false)
|
|
158
|
-
const validationRequestData = React.useRef<RequestMetaData>({requestId: undefined})
|
|
175
|
+
const suggestionRequestData = React.useRef<RequestMetaData>({ requestId: undefined });
|
|
176
|
+
const [pathValidationPending, setPathValidationPending] = React.useState(false);
|
|
177
|
+
const validationRequestData = React.useRef<RequestMetaData>({ requestId: undefined });
|
|
159
178
|
const [, setErrorMarkers] = React.useState<CodeMirror.TextMarker[]>([]);
|
|
160
|
-
const [validationResponse, setValidationResponse] = useState<IValidationResult | undefined>(undefined)
|
|
161
|
-
const [suggestionResponse, setSuggestionResponse] = useState<IPartialAutoCompleteResult | undefined>(undefined)
|
|
179
|
+
const [validationResponse, setValidationResponse] = useState<IValidationResult | undefined>(undefined);
|
|
180
|
+
const [suggestionResponse, setSuggestionResponse] = useState<IPartialAutoCompleteResult | undefined>(undefined);
|
|
162
181
|
// The element that should be used for replacement highlighting
|
|
163
|
-
const [highlightedElement, setHighlightedElement] = useState<ISuggestionWithReplacementInfo | undefined>(undefined)
|
|
182
|
+
const [highlightedElement, setHighlightedElement] = useState<ISuggestionWithReplacementInfo | undefined>(undefined);
|
|
164
183
|
const [editorInstance, setEditorInstance] = React.useState<CodeMirror.Editor>();
|
|
165
184
|
const isFocused = React.useRef(false);
|
|
166
185
|
/** Mutable editor state, since this needs to be current in scope of the SingleLineEditorComponent. */
|
|
167
186
|
const [editorState] = React.useState<{
|
|
168
|
-
index: number
|
|
169
|
-
suggestions: ISuggestionWithReplacementInfo[]
|
|
170
|
-
editorInstance?: CodeMirror.Editor
|
|
171
|
-
dropdownShown: boolean
|
|
172
|
-
}>({index: 0, suggestions: [], dropdownShown: false})
|
|
187
|
+
index: number;
|
|
188
|
+
suggestions: ISuggestionWithReplacementInfo[];
|
|
189
|
+
editorInstance?: CodeMirror.Editor;
|
|
190
|
+
dropdownShown: boolean;
|
|
191
|
+
}>({ index: 0, suggestions: [], dropdownShown: false });
|
|
173
192
|
/** This is for the AutoSuggestionList component in order to re-render. */
|
|
174
|
-
const [focusedIndex, setFocusedIndex] = React.useState(0)
|
|
175
|
-
const selectedTextRanges = React.useRef<IRange[]>([])
|
|
193
|
+
const [focusedIndex, setFocusedIndex] = React.useState(0);
|
|
194
|
+
const selectedTextRanges = React.useRef<IRange[]>([]);
|
|
176
195
|
|
|
177
196
|
const pathIsValid = validationResponse?.valid ?? true;
|
|
178
197
|
|
|
179
198
|
const setCurrentIndex = (newIndex: number) => {
|
|
180
|
-
editorState.index = newIndex
|
|
181
|
-
setFocusedIndex(newIndex)
|
|
182
|
-
}
|
|
183
|
-
const currentIndex = () => editorState.index
|
|
199
|
+
editorState.index = newIndex;
|
|
200
|
+
setFocusedIndex(newIndex);
|
|
201
|
+
};
|
|
202
|
+
const currentIndex = () => editorState.index;
|
|
184
203
|
|
|
185
204
|
React.useEffect(() => {
|
|
186
|
-
editorState.editorInstance = editorInstance
|
|
187
|
-
}, [editorInstance, editorState])
|
|
205
|
+
editorState.editorInstance = editorInstance;
|
|
206
|
+
}, [editorInstance, editorState]);
|
|
188
207
|
|
|
189
208
|
React.useEffect(() => {
|
|
190
|
-
if(initialValue != null) {
|
|
191
|
-
editorInstance?.setValue(initialValue)
|
|
209
|
+
if (initialValue != null) {
|
|
210
|
+
editorInstance?.setValue(initialValue);
|
|
192
211
|
}
|
|
193
|
-
}, [initialValue, editorInstance])
|
|
212
|
+
}, [initialValue, editorInstance]);
|
|
194
213
|
|
|
195
214
|
React.useEffect(() => {
|
|
196
|
-
editorState.dropdownShown = shouldShowDropdown
|
|
197
|
-
}, [shouldShowDropdown, editorState])
|
|
215
|
+
editorState.dropdownShown = shouldShowDropdown;
|
|
216
|
+
}, [shouldShowDropdown, editorState]);
|
|
198
217
|
|
|
199
218
|
// Handle replacement highlighting
|
|
200
219
|
useEffect(() => {
|
|
201
220
|
if (highlightedElement && editorInstance) {
|
|
202
221
|
const { from, length } = highlightedElement;
|
|
203
|
-
if(length > 0 && selectedTextRanges.current.length === 0) {
|
|
222
|
+
if (length > 0 && selectedTextRanges.current.length === 0) {
|
|
204
223
|
const to = from + length;
|
|
205
224
|
const marker = editorInstance.markText(
|
|
206
|
-
{line: 0, ch: from},
|
|
207
|
-
{line: 0, ch: to},
|
|
208
|
-
{className: `${eccgui}-autosuggestion__text--highlighted`}
|
|
225
|
+
{ line: 0, ch: from },
|
|
226
|
+
{ line: 0, ch: to },
|
|
227
|
+
{ className: `${eccgui}-autosuggestion__text--highlighted` }
|
|
209
228
|
);
|
|
210
|
-
return () => marker.clear()
|
|
229
|
+
return () => marker.clear();
|
|
211
230
|
}
|
|
212
231
|
}
|
|
213
232
|
return;
|
|
214
|
-
}, [highlightedElement, selectedTextRanges, editorInstance])
|
|
233
|
+
}, [highlightedElement, selectedTextRanges, editorInstance]);
|
|
215
234
|
|
|
216
235
|
//handle linting
|
|
217
236
|
React.useEffect(() => {
|
|
218
237
|
const parseError = validationResponse?.parseError;
|
|
219
238
|
if (parseError && editorInstance) {
|
|
220
239
|
const { message, start, end } = parseError;
|
|
221
|
-
editorInstance.getDoc().getEditor()
|
|
240
|
+
editorInstance.getDoc().getEditor();
|
|
222
241
|
const marker = editorInstance.markText(
|
|
223
242
|
{ line: 0, ch: start },
|
|
224
243
|
{ line: 0, ch: end },
|
|
225
244
|
{ className: `${eccgui}-autosuggestion__text--highlighted-error`, title: message }
|
|
226
245
|
);
|
|
227
246
|
setErrorMarkers((previousMarkers) => {
|
|
228
|
-
previousMarkers.forEach(marker => marker.clear())
|
|
229
|
-
return [marker]
|
|
247
|
+
previousMarkers.forEach((marker) => marker.clear());
|
|
248
|
+
return [marker];
|
|
230
249
|
});
|
|
231
250
|
} else {
|
|
232
251
|
// Valid, clear all error markers
|
|
233
252
|
setErrorMarkers((previous) => {
|
|
234
|
-
previous.forEach(marker => marker.clear())
|
|
235
|
-
return []
|
|
236
|
-
})
|
|
253
|
+
previous.forEach((marker) => marker.clear());
|
|
254
|
+
return [];
|
|
255
|
+
});
|
|
237
256
|
}
|
|
238
|
-
const isValid = validationResponse?.valid === undefined || validationResponse.valid
|
|
239
|
-
onInputChecked && onInputChecked(isValid)
|
|
257
|
+
const isValid = validationResponse?.valid === undefined || validationResponse.valid;
|
|
258
|
+
onInputChecked && onInputChecked(isValid);
|
|
240
259
|
}, [validationResponse?.valid, validationResponse?.parseError, editorInstance, onInputChecked]);
|
|
241
260
|
|
|
242
261
|
/** generate suggestions and also populate the replacement indexes dict */
|
|
@@ -251,17 +270,22 @@ export const AutoSuggestion = ({
|
|
|
251
270
|
if (suggestionResponse?.replacementResults?.length) {
|
|
252
271
|
suggestionResponse.replacementResults.forEach(
|
|
253
272
|
({ replacements, replacementInterval: { from, length }, extractedQuery }) => {
|
|
254
|
-
const replacementsWithMetaData = replacements.map(r => ({
|
|
273
|
+
const replacementsWithMetaData = replacements.map((r) => ({
|
|
274
|
+
...r,
|
|
275
|
+
query: extractedQuery,
|
|
276
|
+
from,
|
|
277
|
+
length,
|
|
278
|
+
}));
|
|
255
279
|
newSuggestions = [...newSuggestions, ...replacementsWithMetaData];
|
|
256
280
|
}
|
|
257
281
|
);
|
|
258
|
-
editorState.suggestions = newSuggestions
|
|
282
|
+
editorState.suggestions = newSuggestions;
|
|
259
283
|
setSuggestions(newSuggestions);
|
|
260
284
|
} else {
|
|
261
|
-
editorState.suggestions = []
|
|
262
|
-
setSuggestions([])
|
|
285
|
+
editorState.suggestions = [];
|
|
286
|
+
setSuggestions([]);
|
|
263
287
|
}
|
|
264
|
-
editorState.index = 0
|
|
288
|
+
editorState.index = 0;
|
|
265
289
|
}, [suggestionResponse, editorState]);
|
|
266
290
|
|
|
267
291
|
const inputactionsDisplayed = React.useCallback((node) => {
|
|
@@ -273,58 +297,76 @@ export const AutoSuggestion = ({
|
|
|
273
297
|
}
|
|
274
298
|
}, []);
|
|
275
299
|
|
|
276
|
-
const asyncCheckInput = useMemo(
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
300
|
+
const asyncCheckInput = useMemo(
|
|
301
|
+
() => async (inputString: string) => {
|
|
302
|
+
if (
|
|
303
|
+
!checkInput ||
|
|
304
|
+
inputString !== value.current ||
|
|
305
|
+
validationRequestData.current.requestId === inputString
|
|
306
|
+
) {
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
validationRequestData.current.requestId = inputString;
|
|
310
|
+
setPathValidationPending(true);
|
|
311
|
+
try {
|
|
312
|
+
const result: IValidationResult | undefined = await checkInput(inputString);
|
|
313
|
+
setValidationResponse(result);
|
|
314
|
+
} catch (e) {
|
|
315
|
+
setValidationResponse(undefined);
|
|
316
|
+
// TODO: Error handling
|
|
317
|
+
} finally {
|
|
318
|
+
setPathValidationPending(false);
|
|
319
|
+
}
|
|
320
|
+
},
|
|
321
|
+
[checkInput]
|
|
322
|
+
);
|
|
292
323
|
|
|
293
324
|
const checkValuePathValidity = useMemo(
|
|
294
325
|
() => debounce((inputString: string) => asyncCheckInput(inputString), validationRequestDelay),
|
|
295
326
|
[asyncCheckInput, validationRequestDelay]
|
|
296
|
-
)
|
|
327
|
+
);
|
|
297
328
|
|
|
298
|
-
const asyncHandleEditorInputChange = useMemo(
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
suggestionRequestData.current.requestId = requestId
|
|
304
|
-
setSuggestionsPending(true)
|
|
305
|
-
try {
|
|
306
|
-
const result: IPartialAutoCompleteResult | undefined = await fetchSuggestions(inputString, cursorPosition)
|
|
307
|
-
if(value.current === inputString) {
|
|
308
|
-
setSuggestionResponse(result)
|
|
329
|
+
const asyncHandleEditorInputChange = useMemo(
|
|
330
|
+
() => async (inputString: string, cursorPosition: number) => {
|
|
331
|
+
const requestId = `${inputString} ${cursorPosition}`;
|
|
332
|
+
if (requestId === suggestionRequestData.current.requestId) {
|
|
333
|
+
return;
|
|
309
334
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
335
|
+
suggestionRequestData.current.requestId = requestId;
|
|
336
|
+
setSuggestionsPending(true);
|
|
337
|
+
try {
|
|
338
|
+
const result: IPartialAutoCompleteResult | undefined = await fetchSuggestions(
|
|
339
|
+
inputString,
|
|
340
|
+
cursorPosition
|
|
341
|
+
);
|
|
342
|
+
if (value.current === inputString) {
|
|
343
|
+
setSuggestionResponse(result);
|
|
344
|
+
}
|
|
345
|
+
} catch (e) {
|
|
346
|
+
setSuggestionResponse(undefined);
|
|
347
|
+
// TODO: Error handling
|
|
348
|
+
} finally {
|
|
349
|
+
setSuggestionsPending(false);
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
[fetchSuggestions]
|
|
353
|
+
);
|
|
317
354
|
|
|
318
|
-
const handleEditorInputChange = useMemo(
|
|
319
|
-
|
|
355
|
+
const handleEditorInputChange = useMemo(
|
|
356
|
+
() =>
|
|
357
|
+
debounce(
|
|
358
|
+
(inputString: string, cursorPosition: number) =>
|
|
359
|
+
asyncHandleEditorInputChange(inputString, cursorPosition),
|
|
360
|
+
autoCompletionRequestDelay
|
|
361
|
+
),
|
|
320
362
|
[asyncHandleEditorInputChange, autoCompletionRequestDelay]
|
|
321
|
-
)
|
|
363
|
+
);
|
|
322
364
|
|
|
323
365
|
const handleChange = (val: string) => {
|
|
324
|
-
value.current = val
|
|
325
|
-
checkValuePathValidity.cancel()
|
|
326
|
-
checkValuePathValidity(value.current)
|
|
327
|
-
onChange(val)
|
|
366
|
+
value.current = val;
|
|
367
|
+
checkValuePathValidity.cancel();
|
|
368
|
+
checkValuePathValidity(value.current);
|
|
369
|
+
onChange(val);
|
|
328
370
|
};
|
|
329
371
|
|
|
330
372
|
const handleCursorChange = (pos: Position, coords: any, scrollinfo: any) => {
|
|
@@ -332,12 +374,11 @@ export const AutoSuggestion = ({
|
|
|
332
374
|
// cursor change is fired after onChange, so we put the auto-complete logic here
|
|
333
375
|
if (isFocused.current) {
|
|
334
376
|
setShouldShowDropdown(true);
|
|
335
|
-
handleEditorInputChange.cancel()
|
|
336
|
-
handleEditorInputChange(value.current, cursorPosition.current)
|
|
377
|
+
handleEditorInputChange.cancel();
|
|
378
|
+
handleEditorInputChange(value.current, cursorPosition.current);
|
|
337
379
|
}
|
|
338
|
-
horizontalShiftSubscriber.current &&
|
|
339
|
-
Math.min(coords.left, Math.max(coords.left - scrollinfo.left, 0))
|
|
340
|
-
)
|
|
380
|
+
horizontalShiftSubscriber.current &&
|
|
381
|
+
horizontalShiftSubscriber.current(Math.min(coords.left, Math.max(coords.left - scrollinfo.left, 0)));
|
|
341
382
|
};
|
|
342
383
|
|
|
343
384
|
const handleInputEditorKeyPress = (event: KeyboardEvent) => {
|
|
@@ -349,53 +390,57 @@ export const AutoSuggestion = ({
|
|
|
349
390
|
};
|
|
350
391
|
|
|
351
392
|
const closeDropDown = () => {
|
|
352
|
-
setHighlightedElement(undefined)
|
|
353
|
-
setShouldShowDropdown(false)
|
|
354
|
-
}
|
|
393
|
+
setHighlightedElement(undefined);
|
|
394
|
+
setShouldShowDropdown(false);
|
|
395
|
+
};
|
|
355
396
|
|
|
356
397
|
const handleDropdownChange = (selectedSuggestion: ISuggestionWithReplacementInfo) => {
|
|
357
398
|
if (selectedSuggestion && editorState.editorInstance) {
|
|
358
399
|
const { from, length, value } = selectedSuggestion;
|
|
359
400
|
const to = from + length;
|
|
360
|
-
editorState.editorInstance.replaceRange(
|
|
361
|
-
|
|
401
|
+
editorState.editorInstance.replaceRange(
|
|
402
|
+
selectedSuggestion.value,
|
|
403
|
+
{ line: 0, ch: from },
|
|
404
|
+
{ line: 0, ch: to }
|
|
405
|
+
);
|
|
406
|
+
closeDropDown();
|
|
362
407
|
editorState.editorInstance.setCursor({ line: 0, ch: from + value.length });
|
|
363
408
|
editorState.editorInstance.focus();
|
|
364
409
|
}
|
|
365
410
|
};
|
|
366
411
|
|
|
367
412
|
const handleInputEditorClear = () => {
|
|
368
|
-
editorInstance?.setValue("")
|
|
369
|
-
cursorPosition.current = 0
|
|
370
|
-
handleChange("")
|
|
413
|
+
editorInstance?.setValue("");
|
|
414
|
+
cursorPosition.current = 0;
|
|
415
|
+
handleChange("");
|
|
371
416
|
editorInstance?.focus();
|
|
372
417
|
};
|
|
373
418
|
|
|
374
419
|
const handleInputFocus = (focusState: boolean) => {
|
|
375
|
-
onFocusChange && onFocusChange(focusState)
|
|
376
|
-
focusState ? setShouldShowDropdown(true) : closeDropDown()
|
|
377
|
-
if(!isFocused.current && focusState) {
|
|
420
|
+
onFocusChange && onFocusChange(focusState);
|
|
421
|
+
focusState ? setShouldShowDropdown(true) : closeDropDown();
|
|
422
|
+
if (!isFocused.current && focusState) {
|
|
378
423
|
// Just got focus
|
|
379
424
|
// Clear suggestions and repeat suggestion request, something else might have changed while this component was not focused
|
|
380
|
-
setSuggestions([])
|
|
381
|
-
suggestionRequestData.current.requestId = undefined
|
|
382
|
-
isFocused.current = focusState
|
|
383
|
-
handleEditorInputChange.cancel()
|
|
384
|
-
handleEditorInputChange(value.current, cursorPosition.current)
|
|
425
|
+
setSuggestions([]);
|
|
426
|
+
suggestionRequestData.current.requestId = undefined;
|
|
427
|
+
isFocused.current = focusState;
|
|
428
|
+
handleEditorInputChange.cancel();
|
|
429
|
+
handleEditorInputChange(value.current, cursorPosition.current);
|
|
385
430
|
} else {
|
|
386
|
-
isFocused.current = focusState
|
|
431
|
+
isFocused.current = focusState;
|
|
387
432
|
}
|
|
388
433
|
};
|
|
389
434
|
|
|
390
435
|
//keyboard handlers
|
|
391
436
|
const handleArrowDown = () => {
|
|
392
437
|
const lastSuggestionIndex = editorState.suggestions.length - 1;
|
|
393
|
-
setCurrentIndex(currentIndex() === lastSuggestionIndex ? 0 : currentIndex() + 1)
|
|
438
|
+
setCurrentIndex(currentIndex() === lastSuggestionIndex ? 0 : currentIndex() + 1);
|
|
394
439
|
};
|
|
395
440
|
|
|
396
441
|
const handleArrowUp = () => {
|
|
397
442
|
const lastSuggestionIndex = editorState.suggestions.length - 1;
|
|
398
|
-
setCurrentIndex(currentIndex() === 0 ? lastSuggestionIndex : currentIndex() - 1)
|
|
443
|
+
setCurrentIndex(currentIndex() === 0 ? lastSuggestionIndex : currentIndex() - 1);
|
|
399
444
|
};
|
|
400
445
|
|
|
401
446
|
const handleEnterPressed = () => {
|
|
@@ -424,27 +469,37 @@ export const AutoSuggestion = ({
|
|
|
424
469
|
handleTabPressed();
|
|
425
470
|
break;
|
|
426
471
|
default:
|
|
427
|
-
|
|
472
|
+
//do nothing
|
|
428
473
|
}
|
|
429
474
|
}
|
|
430
475
|
};
|
|
431
476
|
|
|
432
477
|
const handleItemHighlighting = React.useCallback((item: ISuggestionWithReplacementInfo | undefined) => {
|
|
433
|
-
setHighlightedElement(item)
|
|
434
|
-
}, [])
|
|
478
|
+
setHighlightedElement(item);
|
|
479
|
+
}, []);
|
|
435
480
|
|
|
436
|
-
const onSelection = React.useMemo(
|
|
437
|
-
|
|
438
|
-
|
|
481
|
+
const onSelection = React.useMemo(
|
|
482
|
+
() => (ranges: IRange[]) => {
|
|
483
|
+
selectedTextRanges.current = ranges;
|
|
484
|
+
},
|
|
485
|
+
[]
|
|
486
|
+
);
|
|
439
487
|
|
|
440
|
-
const subscribeToHorizontalShift = React.useMemo(
|
|
441
|
-
|
|
442
|
-
|
|
488
|
+
const subscribeToHorizontalShift = React.useMemo(
|
|
489
|
+
() => (callback: HorizontalShiftCallbackFunction) => {
|
|
490
|
+
horizontalShiftSubscriber.current = callback;
|
|
491
|
+
},
|
|
492
|
+
[]
|
|
493
|
+
);
|
|
443
494
|
|
|
444
495
|
const hasError = !!value.current && !pathIsValid && !pathValidationPending;
|
|
445
496
|
const autoSuggestionInput = (
|
|
446
|
-
<div id={id} className={`${eccgui}-autosuggestion`}>
|
|
447
|
-
<div
|
|
497
|
+
<div id={id} className={`${eccgui}-autosuggestion` + (className ? ` ${className}` : "")}>
|
|
498
|
+
<div
|
|
499
|
+
className={`${eccgui}-autosuggestion__inputfield ${BlueprintClassNames.INPUT_GROUP} ${
|
|
500
|
+
BlueprintClassNames.FILL
|
|
501
|
+
} ${hasError ? BlueprintClassNames.INTENT_DANGER : ""}`}
|
|
502
|
+
>
|
|
448
503
|
<ContextOverlay
|
|
449
504
|
minimal
|
|
450
505
|
fill
|
|
@@ -452,9 +507,9 @@ export const AutoSuggestion = ({
|
|
|
452
507
|
placement="bottom-start"
|
|
453
508
|
openOnTargetFocus={false}
|
|
454
509
|
autoFocus={false}
|
|
455
|
-
content={
|
|
510
|
+
content={
|
|
456
511
|
<AutoSuggestionList
|
|
457
|
-
id={id+"__dropdown"}
|
|
512
|
+
id={id + "__dropdown"}
|
|
458
513
|
registerForHorizontalShift={subscribeToHorizontalShift}
|
|
459
514
|
loading={suggestionsPending}
|
|
460
515
|
options={suggestions}
|
|
@@ -463,7 +518,7 @@ export const AutoSuggestion = ({
|
|
|
463
518
|
currentlyFocusedIndex={focusedIndex}
|
|
464
519
|
itemToHighlight={handleItemHighlighting}
|
|
465
520
|
/>
|
|
466
|
-
|
|
521
|
+
}
|
|
467
522
|
>
|
|
468
523
|
<SingleLineCodeEditor
|
|
469
524
|
mode="null"
|
|
@@ -493,17 +548,18 @@ export const AutoSuggestion = ({
|
|
|
493
548
|
</div>
|
|
494
549
|
);
|
|
495
550
|
|
|
496
|
-
const withRightElement =
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
<
|
|
501
|
-
{autoSuggestionInput}
|
|
502
|
-
</
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
551
|
+
const withRightElement =
|
|
552
|
+
rightElement || leftElement ? (
|
|
553
|
+
<Toolbar noWrap>
|
|
554
|
+
{leftElement && <ToolbarSection>{leftElement}</ToolbarSection>}
|
|
555
|
+
<ToolbarSection canGrow canShrink>
|
|
556
|
+
<div style={{ minWidth: "100%", maxWidth: "100%" }}>{autoSuggestionInput}</div>
|
|
557
|
+
</ToolbarSection>
|
|
558
|
+
{rightElement && <ToolbarSection>{rightElement}</ToolbarSection>}
|
|
559
|
+
</Toolbar>
|
|
560
|
+
) : (
|
|
561
|
+
autoSuggestionInput
|
|
562
|
+
);
|
|
507
563
|
|
|
508
564
|
return label ? (
|
|
509
565
|
<FieldItem
|
|
@@ -515,14 +571,17 @@ export const AutoSuggestion = ({
|
|
|
515
571
|
{(pathValidationPending || suggestionsPending) && (
|
|
516
572
|
<Spinner size="tiny" position="inline" description="Validating value path" />
|
|
517
573
|
)}
|
|
518
|
-
</>
|
|
574
|
+
</>
|
|
575
|
+
),
|
|
519
576
|
}}
|
|
520
577
|
hasStateDanger={hasError}
|
|
521
578
|
messageText={hasError ? validationErrorText : undefined}
|
|
522
579
|
>
|
|
523
580
|
{withRightElement}
|
|
524
581
|
</FieldItem>
|
|
525
|
-
) :
|
|
582
|
+
) : (
|
|
583
|
+
withRightElement
|
|
584
|
+
);
|
|
526
585
|
};
|
|
527
586
|
|
|
528
587
|
export default AutoSuggestion;
|