@gooddata/sdk-ui-kit 11.53.0-alpha.0 → 11.53.0-alpha.2

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 (124) hide show
  1. package/esm/@ui/UiConfigEditor/ConfigEditorToolbar.d.ts +26 -0
  2. package/esm/@ui/UiConfigEditor/ConfigEditorToolbar.d.ts.map +1 -0
  3. package/esm/@ui/UiConfigEditor/ConfigEditorToolbar.js +38 -0
  4. package/esm/@ui/UiConfigEditor/UiConfigEditor.d.ts +20 -0
  5. package/esm/@ui/UiConfigEditor/UiConfigEditor.d.ts.map +1 -0
  6. package/esm/@ui/UiConfigEditor/UiConfigEditor.js +229 -0
  7. package/esm/@ui/UiConfigEditor/configEditorGrammars.d.ts +15 -0
  8. package/esm/@ui/UiConfigEditor/configEditorGrammars.d.ts.map +1 -0
  9. package/esm/@ui/UiConfigEditor/configEditorGrammars.js +50 -0
  10. package/esm/@ui/UiConfigEditor/configEditorHighlighting.d.ts +6 -0
  11. package/esm/@ui/UiConfigEditor/configEditorHighlighting.d.ts.map +1 -0
  12. package/esm/@ui/UiConfigEditor/configEditorHighlighting.js +31 -0
  13. package/esm/@ui/UiConfigEditor/configEditorLanguage.d.ts +43 -0
  14. package/esm/@ui/UiConfigEditor/configEditorLanguage.d.ts.map +1 -0
  15. package/esm/@ui/UiConfigEditor/configEditorLanguage.js +179 -0
  16. package/esm/@ui/UiConfigEditor/types.d.ts +135 -0
  17. package/esm/@ui/UiConfigEditor/types.d.ts.map +1 -0
  18. package/esm/@ui/UiConfigEditor/types.js +2 -0
  19. package/esm/@ui/UiMenu/components/defaults/DefaultUiMenuHeader.d.ts.map +1 -1
  20. package/esm/@ui/UiMenu/components/defaults/DefaultUiMenuHeader.js +5 -2
  21. package/esm/@ui/UiPagedVirtualList/UiPagedVirtualList.d.ts.map +1 -1
  22. package/esm/@ui/UiPagedVirtualList/UiPagedVirtualList.js +3 -0
  23. package/esm/@ui/UiSubmenuHeader/UiSubmenuHeader.d.ts +2 -2
  24. package/esm/@ui/UiSubmenuHeader/UiSubmenuHeader.d.ts.map +1 -1
  25. package/esm/@ui/UiSubmenuHeader/UiSubmenuHeader.js +1 -0
  26. package/esm/@ui/hooks/useVirtualizerRectObserver.d.ts +12 -0
  27. package/esm/@ui/hooks/useVirtualizerRectObserver.d.ts.map +1 -0
  28. package/esm/@ui/hooks/useVirtualizerRectObserver.js +34 -0
  29. package/esm/Datepicker/Datepicker.d.ts +2 -4
  30. package/esm/Datepicker/Datepicker.d.ts.map +1 -1
  31. package/esm/Datepicker/Datepicker.js +4 -6
  32. package/esm/Dialog/StylingEditorDialog/StylingEditorDialog.d.ts +13 -0
  33. package/esm/Dialog/StylingEditorDialog/StylingEditorDialog.d.ts.map +1 -1
  34. package/esm/Dialog/StylingEditorDialog/StylingEditorDialog.js +19 -4
  35. package/esm/Dropdown/DropdownList.d.ts +7 -1
  36. package/esm/Dropdown/DropdownList.d.ts.map +1 -1
  37. package/esm/Dropdown/DropdownList.js +3 -2
  38. package/esm/FlexDimensions/FlexDimensions.d.ts +2 -20
  39. package/esm/FlexDimensions/FlexDimensions.d.ts.map +1 -1
  40. package/esm/FlexDimensions/FlexDimensions.js +33 -54
  41. package/esm/FlexDimensions/typings.d.ts +8 -0
  42. package/esm/FlexDimensions/typings.d.ts.map +1 -1
  43. package/esm/FlexDimensions/typings.js +1 -1
  44. package/esm/Form/Input.d.ts +2 -33
  45. package/esm/Form/Input.d.ts.map +1 -1
  46. package/esm/Form/Input.js +30 -35
  47. package/esm/Form/InputPure.d.ts +40 -50
  48. package/esm/Form/InputPure.d.ts.map +1 -1
  49. package/esm/Form/InputPure.js +111 -121
  50. package/esm/Form/InputWithNumberFormat.d.ts.map +1 -1
  51. package/esm/Header/Header.d.ts +1 -1
  52. package/esm/Header/Header.d.ts.map +1 -1
  53. package/esm/Header/Header.js +4 -3
  54. package/esm/Header/WorkspacePickerHomeFooter.d.ts +1 -3
  55. package/esm/Header/WorkspacePickerHomeFooter.d.ts.map +1 -1
  56. package/esm/Header/WorkspacePickerHomeFooter.js +6 -6
  57. package/esm/Header/typings.d.ts +0 -2
  58. package/esm/Header/typings.d.ts.map +1 -1
  59. package/esm/List/DateDatasetsListItem.d.ts.map +1 -1
  60. package/esm/List/DateDatasetsListItem.js +1 -2
  61. package/esm/List/LegacyList.d.ts.map +1 -1
  62. package/esm/List/LegacyList.js +2 -20
  63. package/esm/List/List.d.ts.map +1 -1
  64. package/esm/List/List.js +2 -20
  65. package/esm/List/ListItem.d.ts +2 -0
  66. package/esm/List/ListItem.d.ts.map +1 -1
  67. package/esm/List/ListItem.js +2 -2
  68. package/esm/ParameterControl/AllowedValuesParameterControlDropdown.d.ts +22 -0
  69. package/esm/ParameterControl/AllowedValuesParameterControlDropdown.d.ts.map +1 -0
  70. package/esm/ParameterControl/AllowedValuesParameterControlDropdown.js +38 -0
  71. package/esm/ParameterControl/ParameterControl.d.ts +3 -2
  72. package/esm/ParameterControl/ParameterControl.d.ts.map +1 -1
  73. package/esm/ParameterControl/ParameterControl.js +9 -5
  74. package/esm/ShortenedText/ShortenedText.d.ts +10 -14
  75. package/esm/ShortenedText/ShortenedText.d.ts.map +1 -1
  76. package/esm/ShortenedText/ShortenedText.js +85 -96
  77. package/esm/index.d.ts +8 -4
  78. package/esm/index.d.ts.map +1 -1
  79. package/esm/index.js +3 -2
  80. package/esm/sdk-ui-kit.d.ts +264 -170
  81. package/esm/syntaxHighlightingInput/SyntaxHighlightingInput.d.ts +7 -1
  82. package/esm/syntaxHighlightingInput/SyntaxHighlightingInput.d.ts.map +1 -1
  83. package/esm/syntaxHighlightingInput/SyntaxHighlightingInput.js +2 -1
  84. package/esm/syntaxHighlightingInput/hooks/useChangeHandler.d.ts.map +1 -1
  85. package/esm/syntaxHighlightingInput/hooks/useChangeHandler.js +9 -2
  86. package/esm/syntaxHighlightingInput/hooks/useCodemirror.d.ts +4 -1
  87. package/esm/syntaxHighlightingInput/hooks/useCodemirror.d.ts.map +1 -1
  88. package/esm/syntaxHighlightingInput/hooks/useCodemirror.js +2 -2
  89. package/esm/syntaxHighlightingInput/hooks/useCodemirrorChange.d.ts +12 -1
  90. package/esm/syntaxHighlightingInput/hooks/useCodemirrorChange.d.ts.map +1 -1
  91. package/esm/syntaxHighlightingInput/hooks/useCodemirrorChange.js +10 -8
  92. package/esm/syntaxHighlightingInput/hooks/useCodemirrorEditable.d.ts.map +1 -1
  93. package/esm/syntaxHighlightingInput/hooks/useCodemirrorEditable.js +11 -4
  94. package/esm/syntaxHighlightingInput/hooks/useCodemirrorKeymap.d.ts +1 -1
  95. package/esm/syntaxHighlightingInput/hooks/useCodemirrorKeymap.d.ts.map +1 -1
  96. package/esm/syntaxHighlightingInput/hooks/useCodemirrorKeymap.js +26 -22
  97. package/esm/syntaxHighlightingInput/syntaxErrorLinter.d.ts +24 -0
  98. package/esm/syntaxHighlightingInput/syntaxErrorLinter.d.ts.map +1 -0
  99. package/esm/syntaxHighlightingInput/syntaxErrorLinter.js +49 -0
  100. package/package.json +13 -11
  101. package/src/@ui/UiConfigEditor/UiConfigEditor.scss +169 -0
  102. package/src/@ui/UiMenu/UiMenu.scss +6 -0
  103. package/src/@ui/UiSubmenuHeader/UiSubmenuHeader.scss +15 -0
  104. package/src/@ui/index.scss +1 -0
  105. package/src/ParameterControl/ParameterControl.scss +10 -0
  106. package/styles/css/list.css +11 -0
  107. package/styles/css/list.css.map +1 -1
  108. package/styles/css/main.css +150 -1
  109. package/styles/css/main.css.map +1 -1
  110. package/styles/css/menu.css +11 -0
  111. package/styles/css/menu.css.map +1 -1
  112. package/styles/css/stylingEditorDialog.css +5 -0
  113. package/styles/css/stylingEditorDialog.css.map +1 -1
  114. package/styles/css/syntaxHighlightingInput.css +1 -1
  115. package/styles/css/syntaxHighlightingInput.css.map +1 -1
  116. package/styles/scss/list.scss +17 -0
  117. package/styles/scss/stylingEditorDialog.scss +8 -0
  118. package/styles/scss/syntaxHighlightingInput.scss +5 -2
  119. package/esm/syntaxHighlightingInput/YamlEditor.d.ts +0 -35
  120. package/esm/syntaxHighlightingInput/YamlEditor.d.ts.map +0 -1
  121. package/esm/syntaxHighlightingInput/YamlEditor.js +0 -32
  122. package/esm/syntaxHighlightingInput/yamlSyntaxLinter.d.ts +0 -12
  123. package/esm/syntaxHighlightingInput/yamlSyntaxLinter.d.ts.map +0 -1
  124. package/esm/syntaxHighlightingInput/yamlSyntaxLinter.js +0 -32
@@ -1,11 +1,18 @@
1
- // (C) 2025 GoodData Corporation
1
+ // (C) 2025-2026 GoodData Corporation
2
2
  import { useMemo } from "react";
3
+ import { Transaction } from "@codemirror/state";
3
4
  import { EditorView } from "@codemirror/view";
4
5
  export function useChangeHandler({ handleChange, handleCursor, }) {
5
6
  // Create an extension for handling changes
6
7
  const changeHandlerExtension = useMemo(() => {
7
8
  return EditorView.updateListener.of((update) => {
8
- if (update.docChanged) {
9
+ // A change whose every transaction is annotated as remote is the value-sync dispatch
10
+ // from `useCodemirrorChange` echoing the controlled value back in. Reporting it would
11
+ // hand consumers their own value a second time, so each of them would have to keep
12
+ // bookkeeping just to tell the echo from a real edit.
13
+ const isRemoteOnly = update.transactions.length > 0 &&
14
+ update.transactions.every((tr) => tr.annotation(Transaction.remote));
15
+ if (update.docChanged && !isRemoteOnly) {
9
16
  handleChange.current?.(update.state.doc.toString());
10
17
  }
11
18
  if (handleCursor.current && update.selectionSet) {
@@ -1,5 +1,6 @@
1
1
  import { type Extension } from "@codemirror/state";
2
2
  import { EditorView } from "@codemirror/view";
3
+ import { type ExternalChangeSelection } from "./useCodemirrorChange.js";
3
4
  import { type IUseEventHandlersProps } from "./useEventHandlers.js";
4
5
  export interface IUseCodemirrorProps extends IUseEventHandlersProps {
5
6
  value?: string;
@@ -14,8 +15,10 @@ export interface IUseCodemirrorProps extends IUseEventHandlersProps {
14
15
  beforeExtensions?: Extension[];
15
16
  extensions?: Extension[];
16
17
  onApi?: (view: EditorView | null) => void;
18
+ /** Where the selection goes when the controlled value is replaced from outside. */
19
+ externalChangeSelection?: ExternalChangeSelection;
17
20
  }
18
- export declare function useCodemirror({ value, label, disabled, autocompletion, placeholderText, extensions, beforeExtensions, onCompletion, onApi, onKeyDown, onCursor, onChange, onBlur, onFocus }: IUseCodemirrorProps): {
21
+ export declare function useCodemirror({ value, label, disabled, autocompletion, placeholderText, extensions, beforeExtensions, onCompletion, onApi, onKeyDown, onCursor, onChange, onBlur, onFocus, externalChangeSelection }: IUseCodemirrorProps): {
19
22
  editorRef: import("react").RefObject<HTMLDivElement | null>;
20
23
  viewRef: import("react").RefObject<EditorView | null>;
21
24
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useCodemirror.d.ts","sourceRoot":"","sources":["../../../src/syntaxHighlightingInput/hooks/useCodemirror.ts"],"names":[],"mappings":"AAKA,OAAO,EAAe,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,UAAU,EAAiB,MAAM,kBAAkB,CAAC;AAU7D,OAAO,EAAE,KAAK,sBAAsB,EAAoB,MAAM,uBAAuB,CAAC;AAatF,MAAM,WAAW,mBAAoB,SAAQ,sBAAsB;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE;QACb,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,eAAe,CAAC,EAAE,MAAM,CAAC;KAC5B,CAAC;IACF,gBAAgB,CAAC,EAAE,SAAS,EAAE,CAAC;IAC/B,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IACzB,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAC;CAC7C;AAED,wBAAgB,aAAa,CAAC,EAC1B,KAAU,EACV,KAAK,EACL,QAAQ,EACR,cAAc,EACd,eAAe,EACf,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,KAAK,EACL,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,OAAO,EACV,EAAE,mBAAmB;;;EAkFrB"}
1
+ {"version":3,"file":"useCodemirror.d.ts","sourceRoot":"","sources":["../../../src/syntaxHighlightingInput/hooks/useCodemirror.ts"],"names":[],"mappings":"AAKA,OAAO,EAAe,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,UAAU,EAAiB,MAAM,kBAAkB,CAAC;AAK7D,OAAO,EAAE,KAAK,uBAAuB,EAAuB,MAAM,0BAA0B,CAAC;AAK7F,OAAO,EAAE,KAAK,sBAAsB,EAAoB,MAAM,uBAAuB,CAAC;AAatF,MAAM,WAAW,mBAAoB,SAAQ,sBAAsB;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE;QACb,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,eAAe,CAAC,EAAE,MAAM,CAAC;KAC5B,CAAC;IACF,gBAAgB,CAAC,EAAE,SAAS,EAAE,CAAC;IAC/B,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IACzB,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAC;IAC1C,mFAAmF;IACnF,uBAAuB,CAAC,EAAE,uBAAuB,CAAC;CACrD;AAED,wBAAgB,aAAa,CAAC,EAC1B,KAAU,EACV,KAAK,EACL,QAAQ,EACR,cAAc,EACd,eAAe,EACf,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,KAAK,EACL,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,OAAO,EACP,uBAAuB,EAC1B,EAAE,mBAAmB;;;EAkFrB"}
@@ -22,7 +22,7 @@ const customHighlightStyle = HighlightStyle.define([
22
22
  { tag: t.standard(t.variableName), color: "#00c18e", fontWeight: "bold" },
23
23
  { tag: t.keyword, color: "#ab55a3", fontWeight: "bold" },
24
24
  ]);
25
- export function useCodemirror({ value = "", label, disabled, autocompletion, placeholderText, extensions, beforeExtensions, onCompletion, onApi, onKeyDown, onCursor, onChange, onBlur, onFocus, }) {
25
+ export function useCodemirror({ value = "", label, disabled, autocompletion, placeholderText, extensions, beforeExtensions, onCompletion, onApi, onKeyDown, onCursor, onChange, onBlur, onFocus, externalChangeSelection, }) {
26
26
  const editorRef = useRef(null);
27
27
  const viewRef = useRef(null);
28
28
  const { handleCompletion, handleChange, handleKeyDown, handleCursor, handleFocus, handleBlur } = useEventHandlers({
@@ -90,7 +90,7 @@ export function useCodemirror({ value = "", label, disabled, autocompletion, pla
90
90
  };
91
91
  }, []); // eslint-disable-line react-hooks/exhaustive-deps
92
92
  // Handle external value changes
93
- useCodemirrorChange(viewRef, value);
93
+ useCodemirrorChange(viewRef, value, externalChangeSelection);
94
94
  return {
95
95
  editorRef,
96
96
  viewRef,
@@ -1,4 +1,15 @@
1
1
  import { type RefObject } from "react";
2
2
  import { type EditorView } from "@codemirror/view";
3
- export declare function useCodemirrorChange(viewRef: RefObject<EditorView | null>, value: string): void;
3
+ /**
4
+ * Where the selection goes when the controlled value is replaced from outside.
5
+ *
6
+ * - `"clamp"` keeps the current selection, clamped into the new document's bounds.
7
+ * - `"end"` puts the cursor at the end of the new document — for editors whose external replaces
8
+ * mean "a new document" (an applied example, a re-format), where the old offset is meaningless
9
+ * and a subsequent insert-at-cursor should append rather than land at a stale position.
10
+ *
11
+ * @internal
12
+ */
13
+ export type ExternalChangeSelection = "clamp" | "end";
14
+ export declare function useCodemirrorChange(viewRef: RefObject<EditorView | null>, value: string, externalChangeSelection?: ExternalChangeSelection): void;
4
15
  //# sourceMappingURL=useCodemirrorChange.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useCodemirrorChange.d.ts","sourceRoot":"","sources":["../../../src/syntaxHighlightingInput/hooks/useCodemirrorChange.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAa,MAAM,OAAO,CAAC;AAGlD,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,SAAS,CAAC,UAAU,GAAG,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,QAoCvF"}
1
+ {"version":3,"file":"useCodemirrorChange.d.ts","sourceRoot":"","sources":["../../../src/syntaxHighlightingInput/hooks/useCodemirrorChange.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAa,MAAM,OAAO,CAAC;AAGlD,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD;;;;;;;;;GASG;AACH,MAAM,MAAM,uBAAuB,GAAG,OAAO,GAAG,KAAK,CAAC;AAEtD,wBAAgB,mBAAmB,CAC/B,OAAO,EAAE,SAAS,CAAC,UAAU,GAAG,IAAI,CAAC,EACrC,KAAK,EAAE,MAAM,EACb,uBAAuB,GAAE,uBAAiC,QAwC7D"}
@@ -1,7 +1,7 @@
1
- // (C) 2025 GoodData Corporation
1
+ // (C) 2025-2026 GoodData Corporation
2
2
  import { useEffect } from "react";
3
3
  import { EditorSelection, Transaction } from "@codemirror/state";
4
- export function useCodemirrorChange(viewRef, value) {
4
+ export function useCodemirrorChange(viewRef, value, externalChangeSelection = "clamp") {
5
5
  // Handle external value changes
6
6
  useEffect(() => {
7
7
  const view = viewRef.current;
@@ -13,11 +13,13 @@ export function useCodemirrorChange(viewRef, value) {
13
13
  const hasFocus = view.hasFocus;
14
14
  const newLength = value.length;
15
15
  // Adjust selection to stay within bounds of new content
16
- const adjustedSelection = EditorSelection.create(selection.ranges.map((range) => {
17
- const from = Math.min(range.from, newLength);
18
- const to = Math.min(range.to, newLength);
19
- return EditorSelection.range(from, to);
20
- }), selection.mainIndex);
16
+ const adjustedSelection = externalChangeSelection === "end"
17
+ ? EditorSelection.single(newLength)
18
+ : EditorSelection.create(selection.ranges.map((range) => {
19
+ const from = Math.min(range.from, newLength);
20
+ const to = Math.min(range.to, newLength);
21
+ return EditorSelection.range(from, to);
22
+ }), selection.mainIndex);
21
23
  view.dispatch({
22
24
  changes: { from: 0, to: currentValue.length, insert: value },
23
25
  selection: adjustedSelection,
@@ -30,5 +32,5 @@ export function useCodemirrorChange(viewRef, value) {
30
32
  view.focus();
31
33
  }
32
34
  }
33
- }, [value, viewRef]);
35
+ }, [value, viewRef, externalChangeSelection]);
34
36
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useCodemirrorEditable.d.ts","sourceRoot":"","sources":["../../../src/syntaxHighlightingInput/hooks/useCodemirrorEditable.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAqB,MAAM,OAAO,CAAC;AAG1D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,SAAS,CAAC,UAAU,GAAG,IAAI,CAAC,EAAE,QAAQ,CAAC,EAAE,OAAO;;EAqB9F"}
1
+ {"version":3,"file":"useCodemirrorEditable.d.ts","sourceRoot":"","sources":["../../../src/syntaxHighlightingInput/hooks/useCodemirrorEditable.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAqB,MAAM,OAAO,CAAC;AAG1D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAU9C,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,SAAS,CAAC,UAAU,GAAG,IAAI,CAAC,EAAE,QAAQ,CAAC,EAAE,OAAO;;EAqB9F"}
@@ -1,11 +1,18 @@
1
- // (C) 2025 GoodData Corporation
1
+ // (C) 2025-2026 GoodData Corporation
2
2
  import { useEffect, useRef } from "react";
3
- import { Compartment } from "@codemirror/state";
3
+ import { Compartment, EditorState } from "@codemirror/state";
4
4
  import { EditorView } from "@codemirror/view";
5
+ // `EditorView.editable` only governs the DOM's contenteditable, which blocks typing; keymap
6
+ // commands (Enter, deletions, undo) consult `EditorState.readOnly` instead, so both must flip
7
+ // together or a "read-only" editor still mutates from the keyboard.
8
+ const editableExtensions = (disabled) => [
9
+ EditorView.editable.of(!disabled),
10
+ EditorState.readOnly.of(disabled),
11
+ ];
5
12
  export function useCodemirrorEditable(viewRef, disabled) {
6
13
  // Editable compartment
7
14
  const editableCompartmentRef = useRef(new Compartment());
8
- const editableCompartmentExtension = editableCompartmentRef.current.of(EditorView.editable.of(!disabled));
15
+ const editableCompartmentExtension = editableCompartmentRef.current.of(editableExtensions(!!disabled));
9
16
  // Handle disabled state changes
10
17
  useEffect(() => {
11
18
  const view = viewRef.current;
@@ -14,7 +21,7 @@ export function useCodemirrorEditable(viewRef, disabled) {
14
21
  }
15
22
  // Update the editable compartment based on the disabled prop
16
23
  view.dispatch({
17
- effects: editableCompartmentRef.current.reconfigure(EditorView.editable.of(!disabled)),
24
+ effects: editableCompartmentRef.current.reconfigure(editableExtensions(!!disabled)),
18
25
  });
19
26
  }, [disabled, viewRef]);
20
27
  return {
@@ -3,6 +3,6 @@ import { type EditorView } from "@codemirror/view";
3
3
  export declare function useCodemirrorKeymap({ handleKeyDown }: {
4
4
  handleKeyDown: RefObject<((event: KeyboardEvent, view: EditorView) => boolean) | undefined>;
5
5
  }): {
6
- keymapExtension: import("@codemirror/state").Extension;
6
+ keymapExtension: import("@codemirror/state").Extension[];
7
7
  };
8
8
  //# sourceMappingURL=useCodemirrorKeymap.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useCodemirrorKeymap.d.ts","sourceRoot":"","sources":["../../../src/syntaxHighlightingInput/hooks/useCodemirrorKeymap.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAW,MAAM,OAAO,CAAC;AAIhD,OAAO,EAAE,KAAK,UAAU,EAAU,MAAM,kBAAkB,CAAC;AAE3D,wBAAgB,mBAAmB,CAAC,EAChC,aAAa,EAChB,EAAE;IACC,aAAa,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC,GAAG,SAAS,CAAC,CAAC;CAC/F;;EA0BA"}
1
+ {"version":3,"file":"useCodemirrorKeymap.d.ts","sourceRoot":"","sources":["../../../src/syntaxHighlightingInput/hooks/useCodemirrorKeymap.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAW,MAAM,OAAO,CAAC;AAIhD,OAAO,EAAE,KAAK,UAAU,EAAU,MAAM,kBAAkB,CAAC;AAE3D,wBAAgB,mBAAmB,CAAC,EAChC,aAAa,EAChB,EAAE;IACC,aAAa,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC,GAAG,SAAS,CAAC,CAAC;CAC/F;;EA8BA"}
@@ -1,31 +1,35 @@
1
- // (C) 2025 GoodData Corporation
1
+ // (C) 2025-2026 GoodData Corporation
2
2
  import { useMemo } from "react";
3
3
  import { startCompletion } from "@codemirror/autocomplete";
4
- import { defaultKeymap, historyKeymap } from "@codemirror/commands";
4
+ import { defaultKeymap, history, historyKeymap } from "@codemirror/commands";
5
5
  import { keymap } from "@codemirror/view";
6
6
  export function useCodemirrorKeymap({ handleKeyDown, }) {
7
- // Keymap extension
7
+ // Keymap extension. The history state extension comes with it: `historyKeymap` binds the
8
+ // undo/redo commands, but without `history()` installed they have nothing to operate on.
8
9
  const keymapExtension = useMemo(() => {
9
- return keymap.of([
10
- // Handle custom keydown events
11
- {
12
- any(view, event) {
13
- return handleKeyDown.current?.(event, view) ?? false;
10
+ return [
11
+ history(),
12
+ keymap.of([
13
+ // Handle custom keydown events
14
+ {
15
+ any(view, event) {
16
+ return handleKeyDown.current?.(event, view) ?? false;
17
+ },
14
18
  },
15
- },
16
- // "Mod" is Ctrl on Windows/Linux, Cmd on macOS
17
- {
18
- key: "Mod-i",
19
- run: startCompletion,
20
- },
21
- // Ctrl on Windows
22
- {
23
- key: "Ctrl-i",
24
- run: startCompletion,
25
- },
26
- ...defaultKeymap,
27
- ...historyKeymap,
28
- ]);
19
+ // "Mod" is Ctrl on Windows/Linux, Cmd on macOS
20
+ {
21
+ key: "Mod-i",
22
+ run: startCompletion,
23
+ },
24
+ // Ctrl on Windows
25
+ {
26
+ key: "Ctrl-i",
27
+ run: startCompletion,
28
+ },
29
+ ...defaultKeymap,
30
+ ...historyKeymap,
31
+ ]),
32
+ ];
29
33
  }, [handleKeyDown]);
30
34
  return { keymapExtension };
31
35
  }
@@ -0,0 +1,24 @@
1
+ import { type Diagnostic } from "@codemirror/lint";
2
+ import { type EditorState } from "@codemirror/state";
3
+ /**
4
+ * Collects syntax-error diagnostics by walking the parse tree for error nodes.
5
+ *
6
+ * A blank document reports nothing: grammars differ on whether emptiness parses (JSON requires a
7
+ * value, YAML does not), but an editor the user has not typed into yet is not in error — that is
8
+ * what the caller's own "required" validation says, not the gutter.
9
+ *
10
+ * @internal
11
+ */
12
+ export declare function getSyntaxErrorDiagnostics(state: EditorState, message: string): Diagnostic[];
13
+ /**
14
+ * Creates a CodeMirror linter extension that reports syntax errors by walking the parse tree for
15
+ * error nodes.
16
+ *
17
+ * Language-agnostic: it reads the tree the language extension already built for highlighting, so it
18
+ * works for any grammar and costs no extra parse and no extra dependency.
19
+ *
20
+ * @param message - localized message shown on each syntax-error diagnostic
21
+ * @internal
22
+ */
23
+ export declare function createSyntaxErrorLinter(message: string): import("@codemirror/state").Extension;
24
+ //# sourceMappingURL=syntaxErrorLinter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"syntaxErrorLinter.d.ts","sourceRoot":"","sources":["../../src/syntaxHighlightingInput/syntaxErrorLinter.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,UAAU,EAAU,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,EAAE,CA0B3F;AAED;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,yCAEtD"}
@@ -0,0 +1,49 @@
1
+ // (C) 2026 GoodData Corporation
2
+ import { syntaxTree } from "@codemirror/language";
3
+ import { linter } from "@codemirror/lint";
4
+ /**
5
+ * Collects syntax-error diagnostics by walking the parse tree for error nodes.
6
+ *
7
+ * A blank document reports nothing: grammars differ on whether emptiness parses (JSON requires a
8
+ * value, YAML does not), but an editor the user has not typed into yet is not in error — that is
9
+ * what the caller's own "required" validation says, not the gutter.
10
+ *
11
+ * @internal
12
+ */
13
+ export function getSyntaxErrorDiagnostics(state, message) {
14
+ if (state.doc.toString().trim() === "") {
15
+ return [];
16
+ }
17
+ const diagnostics = [];
18
+ const tree = syntaxTree(state);
19
+ tree.iterate({
20
+ enter(node) {
21
+ if (node.type.isError) {
22
+ diagnostics.push({
23
+ from: node.from,
24
+ // Zero-width error nodes are widened so the marker has something to underline —
25
+ // but never past the end of the document, where an incomplete document's EOF
26
+ // error sits: a diagnostic outside the document is out of contract for the lint
27
+ // state and can throw once decorations are built from it.
28
+ to: Math.min(Math.max(node.to, node.from + 1), state.doc.length),
29
+ severity: "error",
30
+ message,
31
+ });
32
+ }
33
+ },
34
+ });
35
+ return diagnostics;
36
+ }
37
+ /**
38
+ * Creates a CodeMirror linter extension that reports syntax errors by walking the parse tree for
39
+ * error nodes.
40
+ *
41
+ * Language-agnostic: it reads the tree the language extension already built for highlighting, so it
42
+ * works for any grammar and costs no extra parse and no extra dependency.
43
+ *
44
+ * @param message - localized message shown on each syntax-error diagnostic
45
+ * @internal
46
+ */
47
+ export function createSyntaxErrorLinter(message) {
48
+ return linter((view) => getSyntaxErrorDiagnostics(view.state, message));
49
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gooddata/sdk-ui-kit",
3
- "version": "11.53.0-alpha.0",
3
+ "version": "11.53.0-alpha.2",
4
4
  "description": "GoodData SDK - UI Building Components",
5
5
  "license": "MIT",
6
6
  "author": "GoodData Corporation",
@@ -38,6 +38,7 @@
38
38
  "@aaronhayes/react-use-hubspot-form": "2.1.2",
39
39
  "@codemirror/autocomplete": "6.20.0",
40
40
  "@codemirror/commands": "6.10.1",
41
+ "@codemirror/lang-json": "6.0.2",
41
42
  "@codemirror/lang-yaml": "6.1.3",
42
43
  "@codemirror/language": "6.12.1",
43
44
  "@codemirror/lint": "6.9.2",
@@ -76,11 +77,12 @@
76
77
  "tslib": "2.8.1",
77
78
  "unified": "^11.0.5",
78
79
  "uuid": "11.1.1",
79
- "@gooddata/sdk-backend-spi": "11.53.0-alpha.0",
80
- "@gooddata/sdk-ui": "11.53.0-alpha.0",
81
- "@gooddata/sdk-model": "11.53.0-alpha.0",
82
- "@gooddata/sdk-ui-theme-provider": "11.53.0-alpha.0",
83
- "@gooddata/util": "11.53.0-alpha.0"
80
+ "yaml": "2.8.3",
81
+ "@gooddata/sdk-backend-spi": "11.53.0-alpha.2",
82
+ "@gooddata/sdk-ui": "11.53.0-alpha.2",
83
+ "@gooddata/sdk-model": "11.53.0-alpha.2",
84
+ "@gooddata/sdk-ui-theme-provider": "11.53.0-alpha.2",
85
+ "@gooddata/util": "11.53.0-alpha.2"
84
86
  },
85
87
  "devDependencies": {
86
88
  "@microsoft/api-documenter": "^7.17.0",
@@ -129,11 +131,11 @@
129
131
  "typescript": "5.9.3",
130
132
  "vitest": "4.1.8",
131
133
  "vitest-dom": "0.1.1",
132
- "@gooddata/eslint-config": "11.53.0-alpha.0",
133
- "@gooddata/oxlint-config": "11.53.0-alpha.0",
134
- "@gooddata/reference-workspace": "11.53.0-alpha.0",
135
- "@gooddata/sdk-backend-mockingbird": "11.53.0-alpha.0",
136
- "@gooddata/stylelint-config": "11.53.0-alpha.0"
134
+ "@gooddata/eslint-config": "11.53.0-alpha.2",
135
+ "@gooddata/oxlint-config": "11.53.0-alpha.2",
136
+ "@gooddata/reference-workspace": "11.53.0-alpha.2",
137
+ "@gooddata/sdk-backend-mockingbird": "11.53.0-alpha.2",
138
+ "@gooddata/stylelint-config": "11.53.0-alpha.2"
137
139
  },
138
140
  "peerDependencies": {
139
141
  "react": "^18.0.0 || ^19.0.0",
@@ -0,0 +1,169 @@
1
+ // (C) 2026 GoodData Corporation
2
+
3
+ @use "sass:color";
4
+
5
+ @use "../../../styles/scss/variables";
6
+
7
+ .gd-ui-kit-config-editor {
8
+ // The single source for the row-height maths: the fixed editor height below and the pinned
9
+ // content line-height must agree, or `rows` stops meaning whole lines.
10
+ --config-editor-line-height: 1.5;
11
+
12
+ display: flex;
13
+ flex: 1;
14
+ flex-direction: column;
15
+ min-height: 0;
16
+ // The frame belongs to the whole control, so the toolbar reads as part of the field rather than
17
+ // as separate chrome floating above it. `overflow: hidden` is what keeps the editor's square
18
+ // corners from poking through the rounded ones.
19
+ border: 1px solid var(--gd-palette-complementary-4);
20
+ border-radius: 3px;
21
+ overflow: hidden;
22
+
23
+ &__toolbar {
24
+ display: flex;
25
+ flex: 0 0 auto;
26
+ align-items: center;
27
+ justify-content: space-between;
28
+ gap: var(--gd-spacing-10px);
29
+ // A fixed height, so the toolbar reads the same whichever controls happen to be in it —
30
+ // radios, the menu button, or just the Auto-format link. 36px of content plus the divider,
31
+ // sized explicitly so a host's global box-sizing reset cannot change the outcome.
32
+ box-sizing: border-box;
33
+ height: 37px;
34
+ padding-left: var(--gd-spacing-10px);
35
+ border-bottom: 1px solid var(--gd-palette-complementary-4);
36
+ }
37
+
38
+ &__languages {
39
+ display: flex;
40
+ align-items: center;
41
+ gap: var(--gd-spacing-10px);
42
+ }
43
+
44
+ &__actions {
45
+ display: flex;
46
+ align-items: center;
47
+ gap: var(--gd-spacing-10px);
48
+ // Keeps the actions pinned to the right edge even when a single-language editor renders no
49
+ // switcher on the left, where `space-between` alone would let them drift to the start.
50
+ margin-left: auto;
51
+ // Mirrors the switcher's inset on the other side.
52
+ padding-right: var(--gd-spacing-10px);
53
+ }
54
+
55
+ // With the menu button present the design sits it flush against the frame — the button's own
56
+ // padding is what keeps the ellipsis off the edge, so any padding here would double it.
57
+ &__actions--withMenu {
58
+ padding-right: 0;
59
+ }
60
+
61
+ &__editor {
62
+ display: flex;
63
+ flex: 1;
64
+ flex-direction: column;
65
+ min-height: 0;
66
+ }
67
+
68
+ /**
69
+ * The shared syntax-highlighting input renders its own wrapper between this element and
70
+ * `.cm-editor`, and that wrapper is a plain block. Without making it part of the flex column the
71
+ * `flex` on `.cm-editor` below is inert, so a long document grows the editor without bound and
72
+ * the surrounding dialog scrolls instead of the editor scrolling inside itself.
73
+ */
74
+ &__editor > * {
75
+ display: flex;
76
+ flex: 1;
77
+ flex-direction: column;
78
+ min-height: 0;
79
+ }
80
+
81
+ /**
82
+ * The shared syntax-highlighting input ships a fixed `height: 100px` on `.cm-editor`, scoped
83
+ * under its own `.gd-input-syntax-highlighting-input` class. That stylesheet is bundled after
84
+ * this one, so at equal specificity it would win — hence the repeated block class, which buys a
85
+ * third class without changing what is matched.
86
+ *
87
+ * Height comes from `rows`, while still growing to fill a taller parent, so the editor works
88
+ * both standalone and as a field in a fixed-height dialog.
89
+ *
90
+ * `--config-editor-rows` is deliberately outside the `--gd-` namespace: that prefix is reserved
91
+ * for theme variables, each of which needs a specification in sdk-ui-theme-provider. This one
92
+ * only carries the `rows` prop into the stylesheet and is not something a theme should set.
93
+ */
94
+ &.gd-ui-kit-config-editor &__editor .cm-editor {
95
+ // A definite height rather than a floor, so the scroller inside always has something to
96
+ // scroll against: with `min-height` alone a long document grows the editor without bound in
97
+ // any parent that is not itself height-constrained, and the page scrolls instead.
98
+ //
99
+ // `rows` whole lines, plus the 8px of padding CodeMirror puts around its content and the 1px
100
+ // border on each side (the box is border-box). Sizing to whole lines keeps a line from being
101
+ // clipped through the middle, which looks unfinished and makes a screenshot of it turn on how
102
+ // half a glyph happens to be antialiased.
103
+ height: calc(var(--config-editor-rows, 12) * var(--config-editor-line-height) * 1em + 8px);
104
+ // Still grows to take the space when a parent does constrain and offer it, which is what a
105
+ // fixed-height dialog does.
106
+ flex: 1 1 auto;
107
+ min-height: 0;
108
+ // The frame and its corners now belong to the wrapper, so the shared input's own border and
109
+ // radius would draw a second box inside it.
110
+ border: none;
111
+ border-radius: 0;
112
+ // The standard input inner shadow, falling from the toolbar onto the code area. It replaces
113
+ // the shared input's own (which doubles as its focus styling — here focus is shown on the
114
+ // whole field instead), with the design's slightly softer blur.
115
+ box-shadow: inset 0 1px 2px 0 color.adjust(variables.$gd-input-text-box-shadow-color, $alpha: -0.85);
116
+ }
117
+
118
+ // Focus is shown on the whole field instead, so the toolbar is visibly part of what is focused.
119
+ &.gd-ui-kit-config-editor:has(.cm-editor.cm-focused) {
120
+ border-color: var(--gd-palette-primary-base);
121
+ }
122
+
123
+ // Pinned rather than left as `normal`, so the height above is exactly `rows` lines.
124
+ &__editor .cm-content {
125
+ line-height: var(--config-editor-line-height);
126
+ }
127
+
128
+ // Scrolling past the end of a long config should not start scrolling the dialog behind it.
129
+ &__editor .cm-scroller {
130
+ overflow: auto;
131
+ overscroll-behavior: contain;
132
+ }
133
+
134
+ /**
135
+ * The design draws no gutter chrome at all: the line numbers sit on the editor's own surface,
136
+ * with no fill and no divider. Making it transparent (rather than a colour) also keeps it
137
+ * correct on a dark theme, where CodeMirror's built-in light-variant gutter would otherwise
138
+ * paint a pale strip — whatever themed background the editor has simply shows through.
139
+ *
140
+ * The repeated block class is for specificity: CodeMirror's own rule is `.cm-editor.cm-light
141
+ * .cm-gutters`, which would otherwise win.
142
+ */
143
+ &.gd-ui-kit-config-editor &__editor .cm-gutters {
144
+ border-right: none;
145
+ background: transparent;
146
+ color: var(--gd-palette-complementary-6);
147
+ }
148
+
149
+ &.gd-ui-kit-config-editor &__editor .cm-activeLineGutter {
150
+ background: transparent;
151
+ color: var(--gd-palette-complementary-7);
152
+ }
153
+
154
+ // Muted surface for a value that cannot be edited. Applied to the whole field, toolbar included,
155
+ // now that they share one frame — tinting only the code area would leave a lighter strip on top.
156
+ // The gutter is transparent, so it takes the same muted surface by itself.
157
+ &--disabled,
158
+ &--readOnly {
159
+ background: var(--gd-palette-complementary-1);
160
+
161
+ .cm-editor {
162
+ background: transparent;
163
+ }
164
+ }
165
+
166
+ &--disabled {
167
+ opacity: 0.7;
168
+ }
169
+ }
@@ -192,6 +192,12 @@
192
192
  > :not(#{$root}__item-title) {
193
193
  flex: none;
194
194
  }
195
+
196
+ // The submenu indicator is an affordance, not content, so it takes the muted colour rather
197
+ // than inheriting the label's.
198
+ .gd-icon-navigateright {
199
+ color: var(--gd-palette-complementary-5);
200
+ }
195
201
  }
196
202
 
197
203
  &__item-title {
@@ -6,6 +6,10 @@
6
6
  align-items: center;
7
7
  gap: var(--gd-spacing-5px);
8
8
  width: 100%;
9
+ // The horizontal padding must come out of the 100% width, not be added on top of it: without
10
+ // this the header renders wider than the menu that contains it and juts out of the panel in any
11
+ // app that does not carry a global border-box reset.
12
+ box-sizing: border-box;
9
13
  color: var(--gd-palette-complementary-6, #94a1ad);
10
14
  background: var(--gd-palette-complementary-2, #ebeff4);
11
15
 
@@ -23,6 +27,17 @@
23
27
  text-transform: uppercase;
24
28
  }
25
29
 
30
+ &--small {
31
+ padding: 0 10px;
32
+ height: 35px;
33
+ min-height: 35px;
34
+
35
+ h3.gd-ui-kit-submenu-header__title-text {
36
+ font-size: 11px;
37
+ line-height: normal;
38
+ }
39
+ }
40
+
26
41
  &--medium {
27
42
  padding: 0 10px;
28
43
  height: 36px;
@@ -51,6 +51,7 @@
51
51
  @use "./UiPermissionMenu/UiPermissionMenu.scss";
52
52
  @use "./UiGranteeRowControls/UiGranteeRowControls.scss";
53
53
  @use "./UiToast/UiToast.scss";
54
+ @use "./UiConfigEditor/UiConfigEditor.scss";
54
55
 
55
56
  // `--gd-font-family` is a `:root` variable only; there is no global `body` font
56
57
  // declaration. Apply it once to every kit root so standalone components don't
@@ -14,6 +14,16 @@
14
14
  0 2px 10px rgba(0, 0, 0, 0.1);
15
15
  }
16
16
 
17
+ &--allowed-values {
18
+ padding: 5px 0;
19
+
20
+ // the flex column prevents the searchfield's bottom margin from collapsing with the
21
+ // list's top margin (.gd-dropdown .gd-infinite-list); keep only the searchfield's
22
+ &:has(.gd-list-searchfield) .gd-infinite-list {
23
+ margin-top: 0;
24
+ }
25
+ }
26
+
17
27
  &__dropdown-field {
18
28
  padding: 10px;
19
29
  }
@@ -670,6 +670,17 @@ button.gd-list-item {
670
670
  color: var(--gd-palette-complementary-8, #464e56);
671
671
  text-decoration: underline;
672
672
  }
673
+ .gd-list-item:has(.gd-list-item-suffix) {
674
+ justify-content: flex-start;
675
+ }
676
+ .gd-list-item:has(.gd-list-item-suffix) > span {
677
+ flex-grow: 0;
678
+ }
679
+ .gd-list-item .gd-list-item-suffix {
680
+ flex: 0 0 auto;
681
+ margin-left: 5px;
682
+ color: var(--gd-palette-complementary-6);
683
+ }
673
684
  .gd-list-item.gd-list-item-separator, .gd-list-item.gd-list-item-header {
674
685
  cursor: default;
675
686
  }