@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.
Files changed (110) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cjs/components/AutoSuggestion/AutoSuggestion.js +61 -19
  3. package/dist/cjs/components/AutoSuggestion/AutoSuggestion.js.map +1 -1
  4. package/dist/cjs/components/AutoSuggestion/AutoSuggestionList.js +4 -18
  5. package/dist/cjs/components/AutoSuggestion/AutoSuggestionList.js.map +1 -1
  6. package/dist/cjs/components/AutoSuggestion/ExtendedCodeEditor.js +65 -0
  7. package/dist/cjs/components/AutoSuggestion/ExtendedCodeEditor.js.map +1 -0
  8. package/dist/cjs/components/AutoSuggestion/index.js +3 -3
  9. package/dist/cjs/components/AutoSuggestion/index.js.map +1 -1
  10. package/dist/cjs/components/Card/CardHeader.js +19 -18
  11. package/dist/cjs/components/Card/CardHeader.js.map +1 -1
  12. package/dist/cjs/components/Grid/GridColumn.js +3 -3
  13. package/dist/cjs/components/Grid/GridColumn.js.map +1 -1
  14. package/dist/cjs/components/PropertyValuePair/PropertyName.js +4 -2
  15. package/dist/cjs/components/PropertyValuePair/PropertyName.js.map +1 -1
  16. package/dist/cjs/components/PropertyValuePair/PropertyValue.js +6 -4
  17. package/dist/cjs/components/PropertyValuePair/PropertyValue.js.map +1 -1
  18. package/dist/cjs/components/PropertyValuePair/PropertyValuePair.js +13 -3
  19. package/dist/cjs/components/PropertyValuePair/PropertyValuePair.js.map +1 -1
  20. package/dist/cjs/components/Skeleton/Skeleton.js +30 -0
  21. package/dist/cjs/components/Skeleton/Skeleton.js.map +1 -0
  22. package/dist/cjs/components/Skeleton/classnames.js +6 -0
  23. package/dist/cjs/components/Skeleton/classnames.js.map +1 -0
  24. package/dist/cjs/components/Table/TableCell.js +3 -2
  25. package/dist/cjs/components/Table/TableCell.js.map +1 -1
  26. package/dist/cjs/components/TextField/useTextValidation.js.map +1 -1
  27. package/dist/cjs/components/index.js +1 -0
  28. package/dist/cjs/components/index.js.map +1 -1
  29. package/dist/cjs/index.js +2 -0
  30. package/dist/cjs/index.js.map +1 -1
  31. package/dist/esm/components/AutoSuggestion/AutoSuggestion.js +82 -40
  32. package/dist/esm/components/AutoSuggestion/AutoSuggestion.js.map +1 -1
  33. package/dist/esm/components/AutoSuggestion/AutoSuggestionList.js +6 -20
  34. package/dist/esm/components/AutoSuggestion/AutoSuggestionList.js.map +1 -1
  35. package/dist/esm/components/AutoSuggestion/ExtendedCodeEditor.js +70 -0
  36. package/dist/esm/components/AutoSuggestion/ExtendedCodeEditor.js.map +1 -0
  37. package/dist/esm/components/AutoSuggestion/index.js +2 -2
  38. package/dist/esm/components/AutoSuggestion/index.js.map +1 -1
  39. package/dist/esm/components/Card/CardHeader.js +15 -14
  40. package/dist/esm/components/Card/CardHeader.js.map +1 -1
  41. package/dist/esm/components/Grid/GridColumn.js +3 -3
  42. package/dist/esm/components/Grid/GridColumn.js.map +1 -1
  43. package/dist/esm/components/PropertyValuePair/PropertyName.js +4 -2
  44. package/dist/esm/components/PropertyValuePair/PropertyName.js.map +1 -1
  45. package/dist/esm/components/PropertyValuePair/PropertyValue.js +6 -4
  46. package/dist/esm/components/PropertyValuePair/PropertyValue.js.map +1 -1
  47. package/dist/esm/components/PropertyValuePair/PropertyValuePair.js +13 -3
  48. package/dist/esm/components/PropertyValuePair/PropertyValuePair.js.map +1 -1
  49. package/dist/esm/components/Skeleton/Skeleton.js +24 -0
  50. package/dist/esm/components/Skeleton/Skeleton.js.map +1 -0
  51. package/dist/esm/components/Skeleton/classnames.js +3 -0
  52. package/dist/esm/components/Skeleton/classnames.js.map +1 -0
  53. package/dist/esm/components/Table/TableCell.js +4 -3
  54. package/dist/esm/components/Table/TableCell.js.map +1 -1
  55. package/dist/esm/components/TextField/useTextValidation.js.map +1 -1
  56. package/dist/esm/components/index.js +1 -0
  57. package/dist/esm/components/index.js.map +1 -1
  58. package/dist/esm/index.js +2 -0
  59. package/dist/esm/index.js.map +1 -1
  60. package/dist/types/components/AutoSuggestion/AutoSuggestion.d.ts +8 -2
  61. package/dist/types/components/AutoSuggestion/AutoSuggestionList.d.ts +6 -4
  62. package/dist/types/components/AutoSuggestion/{SingleLineCodeEditor.d.ts → ExtendedCodeEditor.d.ts} +7 -4
  63. package/dist/types/components/AutoSuggestion/index.d.ts +3 -3
  64. package/dist/types/components/Card/CardHeader.d.ts +2 -2
  65. package/dist/types/components/Grid/GridColumn.d.ts +1 -0
  66. package/dist/types/components/PropertyValuePair/PropertyName.d.ts +7 -1
  67. package/dist/types/components/PropertyValuePair/PropertyValue.d.ts +7 -1
  68. package/dist/types/components/PropertyValuePair/PropertyValuePair.d.ts +5 -1
  69. package/dist/types/components/Skeleton/Skeleton.d.ts +13 -0
  70. package/dist/types/components/Skeleton/classnames.d.ts +1 -0
  71. package/dist/types/components/Table/TableCell.d.ts +7 -2
  72. package/dist/types/components/TextField/useTextValidation.d.ts +1 -0
  73. package/dist/types/components/index.d.ts +1 -0
  74. package/dist/types/index.d.ts +3 -0
  75. package/package.json +1 -1
  76. package/src/components/AutoSuggestion/AutoSuggestion.tsx +84 -31
  77. package/src/components/AutoSuggestion/AutoSuggestionList.tsx +11 -42
  78. package/src/components/AutoSuggestion/ExtendedCodeEditor.tsx +129 -0
  79. package/src/components/AutoSuggestion/index.ts +3 -11
  80. package/src/components/AutoSuggestion/tests/AutoSuggestionList.test.tsx +83 -84
  81. package/src/components/AutoSuggestion/tests/{SingleLineCodeEditor.test.tsx → ExtendedCodeEditor.test.tsx} +7 -7
  82. package/src/components/Card/CardHeader.tsx +23 -25
  83. package/src/components/Grid/GridColumn.tsx +15 -13
  84. package/src/components/Pagination/pagination.scss +6 -1
  85. package/src/components/PropertyValuePair/PropertyName.tsx +21 -2
  86. package/src/components/PropertyValuePair/PropertyValue.tsx +21 -5
  87. package/src/components/PropertyValuePair/PropertyValuePair.tsx +23 -4
  88. package/src/components/PropertyValuePair/propertyvalue.scss +6 -1
  89. package/src/components/PropertyValuePair/stories/PropertyName.stories.tsx +18 -0
  90. package/src/components/PropertyValuePair/stories/PropertyValue.stories.tsx +18 -0
  91. package/src/components/PropertyValuePair/stories/PropertyValueList.stories.tsx +33 -0
  92. package/src/components/PropertyValuePair/stories/PropertyValuePair.stories.tsx +29 -0
  93. package/src/components/Skeleton/Skeleton.stories.tsx +29 -0
  94. package/src/components/Skeleton/Skeleton.tsx +32 -0
  95. package/src/components/Skeleton/classnames.ts +3 -0
  96. package/src/components/Skeleton/skeleton.scss +1 -0
  97. package/src/components/Table/TableCell.tsx +12 -8
  98. package/src/components/Table/stories/TableCell.stories.tsx +30 -0
  99. package/src/components/Table/table.scss +96 -50
  100. package/src/components/TextField/stories/TextField.stories.tsx +21 -18
  101. package/src/components/TextField/useTextValidation.ts +82 -68
  102. package/src/components/index.scss +1 -0
  103. package/src/components/index.ts +1 -0
  104. package/src/extensions/codemirror/_codemirror.scss +1 -0
  105. package/src/index.ts +2 -0
  106. package/dist/cjs/components/AutoSuggestion/SingleLineCodeEditor.js +0 -53
  107. package/dist/cjs/components/AutoSuggestion/SingleLineCodeEditor.js.map +0 -1
  108. package/dist/esm/components/AutoSuggestion/SingleLineCodeEditor.js +0 -47
  109. package/dist/esm/components/AutoSuggestion/SingleLineCodeEditor.js.map +0 -1
  110. 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";
@@ -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.3.1",
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 SingleLineCodeEditor, { IRange } from "./SingleLineCodeEditor";
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 horizontalShiftSubscriber = React.useRef<HorizontalShiftCallbackFunction | undefined>(undefined);
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 result: IPartialAutoCompleteResult | undefined = await fetchSuggestions(
339
- inputString,
340
- cursorPosition
341
- );
342
- if (value.current === inputString) {
343
- setSuggestionResponse(result);
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
- horizontalShiftSubscriber.current &&
381
- horizontalShiftSubscriber.current(Math.min(coords.left, Math.max(coords.left - scrollinfo.left, 0)));
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
- event.preventDefault();
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: 0, ch: from },
404
- { line: 0, ch: to }
435
+ { line: cursor.line, ch: from },
436
+ { line: cursor.line, ch: to }
405
437
  );
406
438
  closeDropDown();
407
- editorState.editorInstance.setCursor({ line: 0, ch: from + value.length });
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 id={id} className={`${eccgui}-autosuggestion` + (className ? ` ${className}` : "")}>
543
+ <div
544
+ id={id}
545
+ ref={autoSuggestionDivRef}
546
+ className={`${eccgui}-autosuggestion` + (className ? ` ${className}` : "")}
547
+ >
498
548
  <div
499
- className={`${eccgui}-autosuggestion__inputfield ${BlueprintClassNames.INPUT_GROUP} ${
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
- registerForHorizontalShift={subscribeToHorizontalShift}
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
- <SingleLineCodeEditor
524
- mode="null"
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 { SingleLineCodeEditor, SingleLineCodeEditorProps } from "./SingleLineCodeEditor";
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 };