@gooddata/sdk-ui-kit 11.42.0-alpha.2 → 11.42.0-alpha.4

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 (59) hide show
  1. package/esm/@ui/@types/icon.d.ts +1 -1
  2. package/esm/@ui/@types/icon.d.ts.map +1 -1
  3. package/esm/@ui/UiAddGranteeDialog/UiAddGranteeDialog.d.ts +6 -31
  4. package/esm/@ui/UiAddGranteeDialog/UiAddGranteeDialog.d.ts.map +1 -1
  5. package/esm/@ui/UiAddGranteeDialog/UiAddGranteeDialog.js +8 -24
  6. package/esm/@ui/UiAddGranteeDialog/UiAddGranteeDialogCard.d.ts +56 -0
  7. package/esm/@ui/UiAddGranteeDialog/UiAddGranteeDialogCard.d.ts.map +1 -0
  8. package/esm/@ui/UiAddGranteeDialog/UiAddGranteeDialogCard.js +40 -0
  9. package/esm/@ui/UiAddGranteeDialog/useGranteeSelection.d.ts +35 -0
  10. package/esm/@ui/UiAddGranteeDialog/useGranteeSelection.d.ts.map +1 -0
  11. package/esm/@ui/UiAddGranteeDialog/useGranteeSelection.js +29 -0
  12. package/esm/@ui/UiAutocomplete/UiAutocomplete.d.ts +9 -0
  13. package/esm/@ui/UiAutocomplete/UiAutocomplete.d.ts.map +1 -0
  14. package/esm/@ui/UiAutocomplete/UiAutocomplete.js +230 -0
  15. package/esm/@ui/UiAutocomplete/types.d.ts +61 -0
  16. package/esm/@ui/UiAutocomplete/types.d.ts.map +1 -0
  17. package/esm/@ui/UiAutocomplete/types.js +2 -0
  18. package/esm/@ui/UiAutocomplete/useAsyncListSource.d.ts +42 -0
  19. package/esm/@ui/UiAutocomplete/useAsyncListSource.d.ts.map +1 -0
  20. package/esm/@ui/UiAutocomplete/useAsyncListSource.js +112 -0
  21. package/esm/@ui/UiCombobox/UiComboboxInput.d.ts +7 -2
  22. package/esm/@ui/UiCombobox/UiComboboxInput.d.ts.map +1 -1
  23. package/esm/@ui/UiCombobox/UiComboboxInput.js +2 -2
  24. package/esm/@ui/UiCombobox/useCombobox.d.ts.map +1 -1
  25. package/esm/@ui/UiCombobox/useCombobox.js +8 -15
  26. package/esm/@ui/UiCombobox/useComboboxChrome.d.ts +16 -5
  27. package/esm/@ui/UiCombobox/useComboboxChrome.d.ts.map +1 -1
  28. package/esm/@ui/UiCombobox/useComboboxChrome.js +53 -25
  29. package/esm/@ui/UiCombobox/useComboboxSelection.d.ts +2 -3
  30. package/esm/@ui/UiCombobox/useComboboxSelection.d.ts.map +1 -1
  31. package/esm/@ui/UiCombobox/useComboboxSelection.js +5 -9
  32. package/esm/@ui/UiGranteeAsyncPicker/UiGranteeAsyncPicker.d.ts +73 -0
  33. package/esm/@ui/UiGranteeAsyncPicker/UiGranteeAsyncPicker.d.ts.map +1 -0
  34. package/esm/@ui/UiGranteeAsyncPicker/UiGranteeAsyncPicker.js +69 -0
  35. package/esm/@ui/UiIcon/icons.d.ts.map +1 -1
  36. package/esm/@ui/UiIcon/icons.js +2 -0
  37. package/esm/@ui/UiTags/UiTags.js +1 -1
  38. package/esm/Dialog/StylingEditorDialog/StylingEditorDialog.d.ts +8 -0
  39. package/esm/Dialog/StylingEditorDialog/StylingEditorDialog.d.ts.map +1 -1
  40. package/esm/Dialog/StylingEditorDialog/StylingEditorDialog.js +28 -3
  41. package/esm/Dropdown/Dropdown.d.ts +4 -2
  42. package/esm/Dropdown/Dropdown.d.ts.map +1 -1
  43. package/esm/Dropdown/Dropdown.js +2 -2
  44. package/esm/WidgetNotice/WidgetNotice.d.ts.map +1 -1
  45. package/esm/WidgetNotice/WidgetNotice.js +1 -1
  46. package/esm/index.d.ts +4 -0
  47. package/esm/index.d.ts.map +1 -1
  48. package/esm/index.js +3 -0
  49. package/esm/locales.d.ts +34 -0
  50. package/esm/locales.d.ts.map +1 -1
  51. package/esm/locales.js +14 -0
  52. package/esm/sdk-ui-kit.d.ts +217 -26
  53. package/package.json +11 -11
  54. package/src/@ui/UiAddGranteeDialog/UiAddGranteeDialog.scss +0 -14
  55. package/src/@ui/UiAutocomplete/UiAutocomplete.scss +53 -0
  56. package/src/@ui/UiGranteeAsyncPicker/UiGranteeAsyncPicker.scss +28 -0
  57. package/src/@ui/index.scss +2 -0
  58. package/styles/css/main.css +65 -5
  59. package/styles/css/main.css.map +1 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * @internal
3
3
  */
4
- export type IconType = "aiAgent" | "aiAgentDisabled" | "brain" | "brainDisabled" | "check" | "checkCircle" | "certification" | "plus" | "plusCircle" | "sync" | "alert" | "alertPaused" | "close" | "cross" | "edit" | "crossCircle" | "question" | "chevronUp" | "chevronRight" | "chevronDown" | "chevronLeft" | "date" | "navigateUp" | "navigateDown" | "navigateRight" | "navigateLeft" | "download" | "slack" | "expand" | "exclamationCircle" | "infoCircle" | "book" | "visible" | "invisible" | "lock" | "unlock" | "ai" | "aiFill" | "drawer" | "drawerEmpty" | "prohibited" | "dropDown" | "dropRight" | "clock" | "clockPaused" | "questionMark" | "upload" | "expandRectangle" | "file" | "number" | "code" | "user" | "userPlus" | "users" | "magic" | "tab" | "pauseCircle" | "filter" | "timer" | "mail" | "envelope" | "copy" | "rain" | "earth" | "geoCollection" | "geoCollectionUpload" | "minimize" | "shrink" | "copyright" | "ellipsis" | "pencil" | "folder" | "folderSmall" | "folderPlus" | "trash" | "arrowUp" | "arrowRight" | "arrowDown" | "arrowLeft" | "undo" | "redo" | "trendDown" | "trendUp" | "save" | "minus" | "minusCircle" | "percent" | "enter" | "enterRight" | "money" | "ghost" | "warning" | "home" | "settings" | "search" | "university" | "printer" | "picture" | "visualization" | "dashboard" | "metric" | "fact" | "ldmAttribute" | "ldmKey" | "ldmLabel" | "sharp" | "attribute" | "horn" | "cw" | "ccw" | "table" | "directionColumn" | "directionRow" | "header" | "genai" | "genai2" | "explainai" | "hiddenForAi" | "box" | "ellipsisVertical" | "list" | "drillTo" | "hierarchy" | "history" | "history2" | "thumbsUp" | "thumbsDown" | "send" | "visualizationArea" | "visualizationTable" | "visualizationTreemap" | "visualizationScatter" | "visualizationDonut" | "visualizationHeadline" | "visualizationColumn" | "visualizationLine" | "visualizationPyramid" | "visualizationFunnel" | "visualizationHeatmap" | "visualizationBubble" | "visualizationPie" | "visualizationBar" | "visualizationCombo" | "visualizationBullet" | "visualizationWaterfall" | "visualizationDependencywheel" | "visualizationSankey" | "visualizationPushpin" | "visualizationRepeater" | "visualizationXirr" | "link" | "externalLink" | "click" | "fileXlsx" | "filePptx" | "filePdf" | "fileImage" | "fileCsvFormatted" | "fileCsvRaw" | "aiDocument" | "recommendation" | "streamUp" | "streamDown" | "stream" | "density" | "parameter" | "pin" | "unpin";
4
+ export type IconType = "aiAgent" | "aiAgentDisabled" | "brain" | "brainDisabled" | "check" | "checkCircle" | "certification" | "plus" | "plusCircle" | "sync" | "alert" | "alertPaused" | "close" | "cross" | "edit" | "crossCircle" | "question" | "chevronUp" | "chevronRight" | "chevronDown" | "chevronLeft" | "date" | "navigateUp" | "navigateDown" | "navigateRight" | "navigateLeft" | "download" | "slack" | "expand" | "exclamationCircle" | "infoCircle" | "book" | "visible" | "invisible" | "lock" | "unlock" | "ai" | "aiFill" | "drawer" | "drawerEmpty" | "prohibited" | "dropDown" | "dropRight" | "clock" | "clockPaused" | "questionMark" | "upload" | "expandRectangle" | "file" | "number" | "code" | "user" | "userPlus" | "users" | "magic" | "tab" | "pauseCircle" | "filter" | "timer" | "mail" | "envelope" | "copy" | "rain" | "earth" | "geoCollection" | "geoCollectionUpload" | "minimize" | "shrink" | "copyright" | "ellipsis" | "pencil" | "folder" | "folderSmall" | "folderPlus" | "trash" | "arrowUp" | "arrowRight" | "arrowDown" | "arrowLeft" | "undo" | "redo" | "trendDown" | "trendUp" | "save" | "minus" | "minusCircle" | "percent" | "enter" | "enterRight" | "money" | "ghost" | "warning" | "home" | "settings" | "search" | "university" | "printer" | "picture" | "visualization" | "dashboard" | "metric" | "fact" | "ldmAttribute" | "ldmKey" | "ldmLabel" | "sharp" | "attribute" | "horn" | "cw" | "ccw" | "table" | "directionColumn" | "directionRow" | "header" | "genai" | "genai2" | "explainai" | "hiddenForAi" | "box" | "ellipsisVertical" | "list" | "drillTo" | "hierarchy" | "history" | "history2" | "thumbsUp" | "thumbsDown" | "send" | "visualizationArea" | "visualizationTable" | "visualizationTreemap" | "visualizationScatter" | "visualizationDonut" | "visualizationHeadline" | "visualizationColumn" | "visualizationLine" | "visualizationPyramid" | "visualizationFunnel" | "visualizationHeatmap" | "visualizationBubble" | "visualizationPie" | "visualizationBar" | "visualizationCombo" | "visualizationBullet" | "visualizationWaterfall" | "visualizationDependencywheel" | "visualizationSankey" | "visualizationPushpin" | "visualizationRepeater" | "visualizationXirr" | "link" | "externalLink" | "click" | "fileXlsx" | "filePptx" | "filePdf" | "fileImage" | "fileCsvFormatted" | "fileCsvRaw" | "aiDocument" | "recommendation" | "streamUp" | "streamDown" | "stream" | "density" | "parameter" | "pin" | "unpin" | "speechBubble" | "pieChart";
5
5
  //# sourceMappingURL=icon.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"icon.d.ts","sourceRoot":"","sources":["../../../src/@ui/@types/icon.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,MAAM,QAAQ,GACd,SAAS,GACT,iBAAiB,GACjB,OAAO,GACP,eAAe,GACf,OAAO,GACP,aAAa,GACb,eAAe,GACf,MAAM,GACN,YAAY,GACZ,MAAM,GACN,OAAO,GACP,aAAa,GACb,OAAO,GACP,OAAO,GACP,MAAM,GACN,aAAa,GACb,UAAU,GACV,WAAW,GACX,cAAc,GACd,aAAa,GACb,aAAa,GACb,MAAM,GACN,YAAY,GACZ,cAAc,GACd,eAAe,GACf,cAAc,GACd,UAAU,GACV,OAAO,GACP,QAAQ,GACR,mBAAmB,GACnB,YAAY,GACZ,MAAM,GACN,SAAS,GACT,WAAW,GACX,MAAM,GACN,QAAQ,GACR,IAAI,GACJ,QAAQ,GACR,QAAQ,GACR,aAAa,GACb,YAAY,GACZ,UAAU,GACV,WAAW,GACX,OAAO,GACP,aAAa,GACb,cAAc,GACd,QAAQ,GACR,iBAAiB,GACjB,MAAM,GACN,QAAQ,GACR,MAAM,GACN,MAAM,GACN,UAAU,GACV,OAAO,GACP,OAAO,GACP,KAAK,GACL,aAAa,GACb,QAAQ,GACR,OAAO,GACP,MAAM,GACN,UAAU,GACV,MAAM,GACN,MAAM,GACN,OAAO,GACP,eAAe,GACf,qBAAqB,GACrB,UAAU,GACV,QAAQ,GACR,WAAW,GACX,UAAU,GACV,QAAQ,GACR,QAAQ,GACR,aAAa,GACb,YAAY,GACZ,OAAO,GACP,SAAS,GACT,YAAY,GACZ,WAAW,GACX,WAAW,GACX,MAAM,GACN,MAAM,GACN,WAAW,GACX,SAAS,GACT,MAAM,GACN,OAAO,GACP,aAAa,GACb,SAAS,GACT,OAAO,GACP,YAAY,GACZ,OAAO,GACP,OAAO,GACP,SAAS,GACT,MAAM,GACN,UAAU,GACV,QAAQ,GACR,YAAY,GACZ,SAAS,GACT,SAAS,GACT,eAAe,GACf,WAAW,GACX,QAAQ,GACR,MAAM,GACN,cAAc,GACd,QAAQ,GACR,UAAU,GACV,OAAO,GACP,WAAW,GACX,MAAM,GACN,IAAI,GACJ,KAAK,GACL,OAAO,GACP,iBAAiB,GACjB,cAAc,GACd,QAAQ,GACR,OAAO,GACP,QAAQ,GACR,WAAW,GACX,aAAa,GACb,KAAK,GACL,kBAAkB,GAClB,MAAM,GACN,SAAS,GACT,WAAW,GACX,SAAS,GACT,UAAU,GACV,UAAU,GACV,YAAY,GACZ,MAAM,GACN,mBAAmB,GACnB,oBAAoB,GACpB,sBAAsB,GACtB,sBAAsB,GACtB,oBAAoB,GACpB,uBAAuB,GACvB,qBAAqB,GACrB,mBAAmB,GACnB,sBAAsB,GACtB,qBAAqB,GACrB,sBAAsB,GACtB,qBAAqB,GACrB,kBAAkB,GAClB,kBAAkB,GAClB,oBAAoB,GACpB,qBAAqB,GACrB,wBAAwB,GACxB,8BAA8B,GAC9B,qBAAqB,GACrB,sBAAsB,GACtB,uBAAuB,GACvB,mBAAmB,GACnB,MAAM,GACN,cAAc,GACd,OAAO,GACP,UAAU,GACV,UAAU,GACV,SAAS,GACT,WAAW,GACX,kBAAkB,GAClB,YAAY,GACZ,YAAY,GACZ,gBAAgB,GAChB,UAAU,GACV,YAAY,GACZ,QAAQ,GACR,SAAS,GACT,WAAW,GACX,KAAK,GACL,OAAO,CAAC"}
1
+ {"version":3,"file":"icon.d.ts","sourceRoot":"","sources":["../../../src/@ui/@types/icon.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,MAAM,QAAQ,GACd,SAAS,GACT,iBAAiB,GACjB,OAAO,GACP,eAAe,GACf,OAAO,GACP,aAAa,GACb,eAAe,GACf,MAAM,GACN,YAAY,GACZ,MAAM,GACN,OAAO,GACP,aAAa,GACb,OAAO,GACP,OAAO,GACP,MAAM,GACN,aAAa,GACb,UAAU,GACV,WAAW,GACX,cAAc,GACd,aAAa,GACb,aAAa,GACb,MAAM,GACN,YAAY,GACZ,cAAc,GACd,eAAe,GACf,cAAc,GACd,UAAU,GACV,OAAO,GACP,QAAQ,GACR,mBAAmB,GACnB,YAAY,GACZ,MAAM,GACN,SAAS,GACT,WAAW,GACX,MAAM,GACN,QAAQ,GACR,IAAI,GACJ,QAAQ,GACR,QAAQ,GACR,aAAa,GACb,YAAY,GACZ,UAAU,GACV,WAAW,GACX,OAAO,GACP,aAAa,GACb,cAAc,GACd,QAAQ,GACR,iBAAiB,GACjB,MAAM,GACN,QAAQ,GACR,MAAM,GACN,MAAM,GACN,UAAU,GACV,OAAO,GACP,OAAO,GACP,KAAK,GACL,aAAa,GACb,QAAQ,GACR,OAAO,GACP,MAAM,GACN,UAAU,GACV,MAAM,GACN,MAAM,GACN,OAAO,GACP,eAAe,GACf,qBAAqB,GACrB,UAAU,GACV,QAAQ,GACR,WAAW,GACX,UAAU,GACV,QAAQ,GACR,QAAQ,GACR,aAAa,GACb,YAAY,GACZ,OAAO,GACP,SAAS,GACT,YAAY,GACZ,WAAW,GACX,WAAW,GACX,MAAM,GACN,MAAM,GACN,WAAW,GACX,SAAS,GACT,MAAM,GACN,OAAO,GACP,aAAa,GACb,SAAS,GACT,OAAO,GACP,YAAY,GACZ,OAAO,GACP,OAAO,GACP,SAAS,GACT,MAAM,GACN,UAAU,GACV,QAAQ,GACR,YAAY,GACZ,SAAS,GACT,SAAS,GACT,eAAe,GACf,WAAW,GACX,QAAQ,GACR,MAAM,GACN,cAAc,GACd,QAAQ,GACR,UAAU,GACV,OAAO,GACP,WAAW,GACX,MAAM,GACN,IAAI,GACJ,KAAK,GACL,OAAO,GACP,iBAAiB,GACjB,cAAc,GACd,QAAQ,GACR,OAAO,GACP,QAAQ,GACR,WAAW,GACX,aAAa,GACb,KAAK,GACL,kBAAkB,GAClB,MAAM,GACN,SAAS,GACT,WAAW,GACX,SAAS,GACT,UAAU,GACV,UAAU,GACV,YAAY,GACZ,MAAM,GACN,mBAAmB,GACnB,oBAAoB,GACpB,sBAAsB,GACtB,sBAAsB,GACtB,oBAAoB,GACpB,uBAAuB,GACvB,qBAAqB,GACrB,mBAAmB,GACnB,sBAAsB,GACtB,qBAAqB,GACrB,sBAAsB,GACtB,qBAAqB,GACrB,kBAAkB,GAClB,kBAAkB,GAClB,oBAAoB,GACpB,qBAAqB,GACrB,wBAAwB,GACxB,8BAA8B,GAC9B,qBAAqB,GACrB,sBAAsB,GACtB,uBAAuB,GACvB,mBAAmB,GACnB,MAAM,GACN,cAAc,GACd,OAAO,GACP,UAAU,GACV,UAAU,GACV,SAAS,GACT,WAAW,GACX,kBAAkB,GAClB,YAAY,GACZ,YAAY,GACZ,gBAAgB,GAChB,UAAU,GACV,YAAY,GACZ,QAAQ,GACR,SAAS,GACT,WAAW,GACX,KAAK,GACL,OAAO,GACP,cAAc,GACd,UAAU,CAAC"}
@@ -1,42 +1,17 @@
1
- import { type ReactNode } from "react";
1
+ import { type IUiAddGranteeDialogCardProps } from "./UiAddGranteeDialogCard.js";
2
2
  /**
3
3
  * @internal
4
4
  */
5
- export interface IUiAddGranteeDialogProps {
5
+ export interface IUiAddGranteeDialogProps extends IUiAddGranteeDialogCardProps {
6
6
  /** Whether the dialog is shown. */
7
7
  isOpen: boolean;
8
- /** Object title shown in the header — wrapped into `Share "\{title\}"`. */
9
- objectTitle: string;
10
- /** Current search query. */
11
- searchQuery: string;
12
- /** Fires when the user edits the search query. */
13
- onSearchQueryChange: (next: string) => void;
14
- /**
15
- * Optional slot rendered between the search input and the bottom divider.
16
- * When empty, the screen falls back to a "No user or group selected"
17
- * placeholder. Callers typically render a single `UiGranteeRow`
18
- * here to preview the grantee they've picked from the search dropdown.
19
- */
20
- selectedGrantee?: ReactNode;
21
- /** Fires when the user clicks the header back-arrow button. */
22
- onBack: () => void;
23
- /** Fires when the user clicks the header X close button or dismisses the modal. */
24
- onClose: () => void;
25
- /** Fires when the user clicks Cancel in the footer. */
26
- onCancel: () => void;
27
- /** Fires when the user clicks the primary Add button in the footer. */
28
- onAdd: () => void;
29
- /** When true, the primary Add button is disabled. */
30
- isAddDisabled?: boolean;
31
- /** Test id forwarded to the modal overlay. */
32
- dataTestId?: string;
33
8
  }
34
9
  /**
35
- * Modal dialog for adding a grantee, opened from the share dialog's
36
- * "+ Add" action. Lets the author search for a user or group, preview the
37
- * grantee they picked, and confirm adding them with the footer Add button.
10
+ * Add-grantee dialog wraps `UiAddGranteeDialogCard` in `UiModalDialog`
11
+ * for the full modal contract (portal, dimmed backdrop, focus trap, Esc
12
+ * and backdrop dismiss).
38
13
  *
39
14
  * @internal
40
15
  */
41
- export declare function UiAddGranteeDialog({ isOpen, objectTitle, searchQuery, onSearchQueryChange, selectedGrantee, onBack, onClose, onCancel, onAdd, isAddDisabled, dataTestId }: IUiAddGranteeDialogProps): import("react/jsx-runtime").JSX.Element;
16
+ export declare function UiAddGranteeDialog({ isOpen, ...cardProps }: IUiAddGranteeDialogProps): import("react/jsx-runtime").JSX.Element;
42
17
  //# sourceMappingURL=UiAddGranteeDialog.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"UiAddGranteeDialog.d.ts","sourceRoot":"","sources":["../../../src/@ui/UiAddGranteeDialog/UiAddGranteeDialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAmBvC;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACrC,mCAAmC;IACnC,MAAM,EAAE,OAAO,CAAC;IAChB,6EAA2E;IAC3E,WAAW,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C;;;;;OAKG;IACH,eAAe,CAAC,EAAE,SAAS,CAAC;IAE5B,+DAA+D;IAC/D,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,mFAAmF;IACnF,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,uDAAuD;IACvD,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,uEAAuE;IACvE,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,qDAAqD;IACrD,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB,8CAA8C;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,EAC/B,MAAM,EACN,WAAW,EACX,WAAW,EACX,mBAAmB,EACnB,eAAe,EACf,MAAM,EACN,OAAO,EACP,QAAQ,EACR,KAAK,EACL,aAAa,EACb,UAAU,EACb,EAAE,wBAAwB,2CAmD1B"}
1
+ {"version":3,"file":"UiAddGranteeDialog.d.ts","sourceRoot":"","sources":["../../../src/@ui/UiAddGranteeDialog/UiAddGranteeDialog.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,4BAA4B,EAA0B,MAAM,6BAA6B,CAAC;AAExG;;GAEG;AACH,MAAM,WAAW,wBAAyB,SAAQ,4BAA4B;IAC1E,mCAAmC;IACnC,MAAM,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,EAAE,EAAE,wBAAwB,2CAMpF"}
@@ -1,30 +1,14 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useIntl } from "react-intl";
3
- import { commonDialogMessages, olpAddGranteeDialogMessages, olpObjectShareDialogMessages, } from "../../locales.js";
4
- import { bem } from "../@utils/bem.js";
5
- import { UiButton } from "../UiButton/UiButton.js";
6
- import { UiIconButton } from "../UiIconButton/UiIconButton.js";
7
- import { UiDialogFooter } from "../UiModalDialog/UiDialogFooter.js";
8
- import { UiDialogHeader } from "../UiModalDialog/UiDialogHeader.js";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // (C) 2026 GoodData Corporation
9
3
  import { UiModalDialog } from "../UiModalDialog/UiModalDialog.js";
10
- import { UiTextInput } from "../UiTextInput/UiTextInput.js";
11
- const { b, e } = bem("gd-ui-kit-add-grantee-dialog");
4
+ import { UiAddGranteeDialogCard } from "./UiAddGranteeDialogCard.js";
12
5
  /**
13
- * Modal dialog for adding a grantee, opened from the share dialog's
14
- * "+ Add" action. Lets the author search for a user or group, preview the
15
- * grantee they picked, and confirm adding them with the footer Add button.
6
+ * Add-grantee dialog wraps `UiAddGranteeDialogCard` in `UiModalDialog`
7
+ * for the full modal contract (portal, dimmed backdrop, focus trap, Esc
8
+ * and backdrop dismiss).
16
9
  *
17
10
  * @internal
18
11
  */
19
- export function UiAddGranteeDialog({ isOpen, objectTitle, searchQuery, onSearchQueryChange, selectedGrantee, onBack, onClose, onCancel, onAdd, isAddDisabled, dataTestId, }) {
20
- const intl = useIntl();
21
- const dialogTitle = intl.formatMessage(olpObjectShareDialogMessages.title, { title: objectTitle });
22
- const backButton = (_jsx(UiIconButton, { icon: "chevronLeft", variant: "tertiary", size: "small", onClick: onBack, accessibilityConfig: { ariaLabel: intl.formatMessage(olpAddGranteeDialogMessages.back) } }));
23
- return (_jsxs(UiModalDialog, { isOpen: isOpen, onClose: onClose, dataTestId: dataTestId, children: [
24
- _jsx(UiDialogHeader, { title: dialogTitle, titleSize: "large", onClose: onClose, leading: backButton }), _jsxs("div", { className: b(), children: [
25
- _jsx(UiTextInput, { type: "search", value: searchQuery, onChange: onSearchQueryChange, label: intl.formatMessage(olpAddGranteeDialogMessages.userOrGroup), placeholder: intl.formatMessage(olpAddGranteeDialogMessages.searchPlaceholder) }), _jsx("div", { className: e("preview"), children: selectedGrantee ?? (_jsx("span", { className: e("empty-state"), children: intl.formatMessage(olpAddGranteeDialogMessages.emptyState) })) })
26
- ] }), _jsxs(UiDialogFooter, { divider: true, children: [
27
- _jsx(UiButton, { label: intl.formatMessage(commonDialogMessages.cancel), variant: "secondary", size: "medium", onClick: onCancel }), _jsx(UiButton, { label: intl.formatMessage(olpAddGranteeDialogMessages.add), variant: "primary", size: "medium", onClick: onAdd, isDisabled: isAddDisabled })
28
- ] })
29
- ] }));
12
+ export function UiAddGranteeDialog({ isOpen, ...cardProps }) {
13
+ return (_jsx(UiModalDialog, { isOpen: isOpen, onClose: cardProps.onClose, children: _jsx(UiAddGranteeDialogCard, { ...cardProps }) }));
30
14
  }
@@ -0,0 +1,56 @@
1
+ import { type IUiGranteeAsyncOptions, type IUiPickedGrantee } from "../UiGranteeAsyncPicker/UiGranteeAsyncPicker.js";
2
+ import { type PermissionMenuLevel } from "../UiPermissionMenu/UiPermissionMenu.js";
3
+ /**
4
+ * @internal
5
+ */
6
+ export interface IUiAddGranteeDialogCardProps {
7
+ /** Object title shown in the header — wrapped into `Share "\{title\}"`. */
8
+ objectTitle: string;
9
+ /** Loader passed straight through to the embedded `UiGranteeAsyncPicker`. */
10
+ loadOptions: (search: string) => Promise<IUiGranteeAsyncOptions>;
11
+ /**
12
+ * Grantees currently picked. Single source of truth — the card derives all
13
+ * row state from it and emits the next list via `onSelectedGranteesChange`.
14
+ */
15
+ selectedGrantees: ReadonlyArray<IUiPickedGrantee>;
16
+ /**
17
+ * Fires with the next list when the user picks a grantee, changes a row's
18
+ * permission level, or removes a row. Consumers store the result in their
19
+ * own state.
20
+ */
21
+ onSelectedGranteesChange: (next: IUiPickedGrantee[]) => void;
22
+ /**
23
+ * Permission level used when a grantee is added from the dropdown.
24
+ * Defaults to `"VIEW"`. Consumers that need a different default (e.g.
25
+ * "SHARE" for owners) override here.
26
+ */
27
+ initialPermissionLevel?: PermissionMenuLevel;
28
+ /** Fires when the user clicks the header back-arrow button. */
29
+ onBack: () => void;
30
+ /** Fires when the user clicks the header X close button. */
31
+ onClose: () => void;
32
+ /** Fires when the user clicks Cancel in the footer. */
33
+ onCancel: () => void;
34
+ /**
35
+ * Fires when the user clicks the primary Share button in the footer. The
36
+ * button is disabled while no grantee has been picked.
37
+ */
38
+ onShare: () => void;
39
+ /** Test id forwarded to the root element. */
40
+ dataTestId?: string;
41
+ }
42
+ /**
43
+ * Add-grantee dialog card — header + grantee picker (search input with
44
+ * sectioned Groups/Users dropdown and picked-rows list) + footer with Cancel
45
+ * and Share. Renders inline as a plain card. For modal behavior (portal,
46
+ * backdrop, focus trap, dismiss), use `UiAddGranteeDialog` which wraps this
47
+ * in `UiModalDialog`.
48
+ *
49
+ * The card owns the visual composition (picker layout) but no policy. The
50
+ * caller controls the picked list via `selectedGrantees` / `onSelectedGranteesChange`,
51
+ * and may override the initial permission level via `initialPermissionLevel`.
52
+ *
53
+ * @internal
54
+ */
55
+ export declare function UiAddGranteeDialogCard({ objectTitle, loadOptions, selectedGrantees, onSelectedGranteesChange, initialPermissionLevel, onBack, onClose, onCancel, onShare, dataTestId }: IUiAddGranteeDialogCardProps): import("react/jsx-runtime").JSX.Element;
56
+ //# sourceMappingURL=UiAddGranteeDialogCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UiAddGranteeDialogCard.d.ts","sourceRoot":"","sources":["../../../src/@ui/UiAddGranteeDialog/UiAddGranteeDialogCard.tsx"],"names":[],"mappings":"AAWA,OAAO,EACH,KAAK,sBAAsB,EAC3B,KAAK,gBAAgB,EAExB,MAAM,iDAAiD,CAAC;AAIzD,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,yCAAyC,CAAC;AAMnF;;GAEG;AACH,MAAM,WAAW,4BAA4B;IACzC,6EAA2E;IAC3E,WAAW,EAAE,MAAM,CAAC;IAEpB,6EAA6E;IAC7E,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,sBAAsB,CAAC,CAAC;IACjE;;;OAGG;IACH,gBAAgB,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAClD;;;;OAIG;IACH,wBAAwB,EAAE,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;IAC7D;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,mBAAmB,CAAC;IAE7C,+DAA+D;IAC/D,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,4DAA4D;IAC5D,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,uDAAuD;IACvD,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB;;;OAGG;IACH,OAAO,EAAE,MAAM,IAAI,CAAC;IAEpB,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,EACnC,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,wBAAwB,EACxB,sBAA+B,EAC/B,MAAM,EACN,OAAO,EACP,QAAQ,EACR,OAAO,EACP,UAAU,EACb,EAAE,4BAA4B,2CAgD9B"}
@@ -0,0 +1,40 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // (C) 2026 GoodData Corporation
3
+ import { useIntl } from "react-intl";
4
+ import { commonDialogMessages, olpAddGranteeDialogMessages, olpObjectShareDialogMessages, } from "../../locales.js";
5
+ import { bem } from "../@utils/bem.js";
6
+ import { UiButton } from "../UiButton/UiButton.js";
7
+ import { UiGranteeAsyncPicker, } from "../UiGranteeAsyncPicker/UiGranteeAsyncPicker.js";
8
+ import { UiIconButton } from "../UiIconButton/UiIconButton.js";
9
+ import { UiDialogFooter } from "../UiModalDialog/UiDialogFooter.js";
10
+ import { UiDialogHeader } from "../UiModalDialog/UiDialogHeader.js";
11
+ import { useGranteeSelection } from "./useGranteeSelection.js";
12
+ const { b } = bem("gd-ui-kit-add-grantee-dialog");
13
+ /**
14
+ * Add-grantee dialog card — header + grantee picker (search input with
15
+ * sectioned Groups/Users dropdown and picked-rows list) + footer with Cancel
16
+ * and Share. Renders inline as a plain card. For modal behavior (portal,
17
+ * backdrop, focus trap, dismiss), use `UiAddGranteeDialog` which wraps this
18
+ * in `UiModalDialog`.
19
+ *
20
+ * The card owns the visual composition (picker layout) but no policy. The
21
+ * caller controls the picked list via `selectedGrantees` / `onSelectedGranteesChange`,
22
+ * and may override the initial permission level via `initialPermissionLevel`.
23
+ *
24
+ * @internal
25
+ */
26
+ export function UiAddGranteeDialogCard({ objectTitle, loadOptions, selectedGrantees, onSelectedGranteesChange, initialPermissionLevel = "VIEW", onBack, onClose, onCancel, onShare, dataTestId, }) {
27
+ const intl = useIntl();
28
+ const dialogTitle = intl.formatMessage(olpObjectShareDialogMessages.title, { title: objectTitle });
29
+ const backButton = (_jsx(UiIconButton, { icon: "chevronLeft", variant: "tertiary", size: "small", onClick: onBack, accessibilityConfig: { ariaLabel: intl.formatMessage(olpAddGranteeDialogMessages.back) } }));
30
+ const { select, changePermission, remove } = useGranteeSelection({
31
+ selectedGrantees,
32
+ onSelectedGranteesChange,
33
+ initialPermissionLevel,
34
+ });
35
+ return (_jsxs("div", { className: b(), "data-testid": dataTestId, children: [
36
+ _jsx(UiDialogHeader, { title: dialogTitle, titleSize: "large", onClose: onClose, leading: backButton }), _jsx(UiGranteeAsyncPicker, { loadOptions: loadOptions, selectedGrantees: selectedGrantees, onSelect: select, onPermissionChange: changePermission, onRemove: remove }), _jsxs(UiDialogFooter, { divider: true, children: [
37
+ _jsx(UiButton, { label: intl.formatMessage(commonDialogMessages.cancel), variant: "secondary", size: "medium", onClick: onCancel }), _jsx(UiButton, { label: intl.formatMessage(olpAddGranteeDialogMessages.add), variant: "primary", size: "medium", onClick: onShare, isDisabled: selectedGrantees.length === 0 })
38
+ ] })
39
+ ] }));
40
+ }
@@ -0,0 +1,35 @@
1
+ import { type IUiGranteeAsyncOption, type IUiPickedGrantee } from "../UiGranteeAsyncPicker/UiGranteeAsyncPicker.js";
2
+ import { type PermissionMenuLevel } from "../UiPermissionMenu/UiPermissionMenu.js";
3
+ /**
4
+ * @internal
5
+ */
6
+ export interface IUseGranteeSelectionOptions {
7
+ /** Current picked list — caller owns the state. */
8
+ selectedGrantees: ReadonlyArray<IUiPickedGrantee>;
9
+ /** Fires with the next list whenever the hook produces a change. */
10
+ onSelectedGranteesChange: (next: IUiPickedGrantee[]) => void;
11
+ /**
12
+ * Permission level used when a grantee is added from the picker.
13
+ * Defaults to `"VIEW"`.
14
+ */
15
+ initialPermissionLevel?: PermissionMenuLevel;
16
+ }
17
+ /**
18
+ * @internal
19
+ */
20
+ export interface IUseGranteeSelectionResult {
21
+ /** Pick a grantee from the picker — no-op if already in the list. */
22
+ select: (option: IUiGranteeAsyncOption) => void;
23
+ /** Change a row's permission level. */
24
+ changePermission: (grantee: IUiPickedGrantee, next: PermissionMenuLevel) => void;
25
+ /** Remove a row. */
26
+ remove: (grantee: IUiPickedGrantee) => void;
27
+ }
28
+ /**
29
+ * Selection callbacks for the add-grantee dialog. Caller owns the list and
30
+ * receives the next list via `onSelectedGranteesChange`.
31
+ *
32
+ * @internal
33
+ */
34
+ export declare function useGranteeSelection({ selectedGrantees, onSelectedGranteesChange, initialPermissionLevel }: IUseGranteeSelectionOptions): IUseGranteeSelectionResult;
35
+ //# sourceMappingURL=useGranteeSelection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useGranteeSelection.d.ts","sourceRoot":"","sources":["../../../src/@ui/UiAddGranteeDialog/useGranteeSelection.ts"],"names":[],"mappings":"AAIA,OAAO,EACH,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACxB,MAAM,iDAAiD,CAAC;AACzD,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,yCAAyC,CAAC;AAEnF;;GAEG;AACH,MAAM,WAAW,2BAA2B;IACxC,qDAAmD;IACnD,gBAAgB,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAClD,oEAAoE;IACpE,wBAAwB,EAAE,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;IAC7D;;;OAGG;IACH,sBAAsB,CAAC,EAAE,mBAAmB,CAAC;CAChD;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACvC,uEAAqE;IACrE,MAAM,EAAE,CAAC,MAAM,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAChD,uCAAuC;IACvC,gBAAgB,EAAE,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACjF,oBAAoB;IACpB,MAAM,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,CAAC;CAC/C;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,EAChC,gBAAgB,EAChB,wBAAwB,EACxB,sBAA+B,EAClC,EAAE,2BAA2B,GAAG,0BAA0B,CAkC1D"}
@@ -0,0 +1,29 @@
1
+ // (C) 2026 GoodData Corporation
2
+ import { useCallback } from "react";
3
+ /**
4
+ * Selection callbacks for the add-grantee dialog. Caller owns the list and
5
+ * receives the next list via `onSelectedGranteesChange`.
6
+ *
7
+ * @internal
8
+ */
9
+ export function useGranteeSelection({ selectedGrantees, onSelectedGranteesChange, initialPermissionLevel = "VIEW", }) {
10
+ const select = useCallback((option) => {
11
+ // The picker already filters out picked ids, but a quick double-click
12
+ // can land a second select call before the parent re-renders — guard
13
+ // against the duplicate row here.
14
+ if (selectedGrantees.some((g) => g.id === option.id)) {
15
+ return;
16
+ }
17
+ onSelectedGranteesChange([
18
+ ...selectedGrantees,
19
+ { ...option, permissionLevel: initialPermissionLevel },
20
+ ]);
21
+ }, [selectedGrantees, onSelectedGranteesChange, initialPermissionLevel]);
22
+ const changePermission = useCallback((grantee, next) => {
23
+ onSelectedGranteesChange(selectedGrantees.map((g) => (g.id === grantee.id ? { ...g, permissionLevel: next } : g)));
24
+ }, [selectedGrantees, onSelectedGranteesChange]);
25
+ const remove = useCallback((grantee) => {
26
+ onSelectedGranteesChange(selectedGrantees.filter((g) => g.id !== grantee.id));
27
+ }, [selectedGrantees, onSelectedGranteesChange]);
28
+ return { select, changePermission, remove };
29
+ }
@@ -0,0 +1,9 @@
1
+ import type { IUiAutocompleteOption, IUiAutocompleteProps } from "./types.js";
2
+ /**
3
+ * Async autocomplete: text input + sectioned listbox driven by a `loadOptions`
4
+ * loader. Implements the ARIA combobox+listbox APG pattern.
5
+ *
6
+ * @internal
7
+ */
8
+ export declare function UiAutocomplete<T extends IUiAutocompleteOption = IUiAutocompleteOption>({ loadOptions, selectedIds, onSelect, debounceMs, messages, accessibilityConfig, dataTestId }: IUiAutocompleteProps<T>): import("react/jsx-runtime").JSX.Element;
9
+ //# sourceMappingURL=UiAutocomplete.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UiAutocomplete.d.ts","sourceRoot":"","sources":["../../../src/@ui/UiAutocomplete/UiAutocomplete.tsx"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,qBAAqB,EAAE,oBAAoB,EAA0B,MAAM,YAAY,CAAC;AA+BtG;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,qBAAqB,GAAG,qBAAqB,EAAE,EACpF,WAAW,EACX,WAA6B,EAC7B,QAAQ,EACR,UAAgC,EAChC,QAAQ,EACR,mBAAmB,EACnB,UAAU,EACb,EAAE,oBAAoB,CAAC,CAAC,CAAC,2CAkOzB"}
@@ -0,0 +1,230 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // (C) 2026 GoodData Corporation
3
+ import { Fragment, useCallback, useId, useMemo } from "react";
4
+ import { useIntl } from "react-intl";
5
+ import { uiAutocompleteMessages } from "../../locales.js";
6
+ import { bem } from "../@utils/bem.js";
7
+ import { makeKeyboardNavigation } from "../@utils/keyboardNavigation.js";
8
+ import { UiComboboxContextProvider } from "../UiCombobox/UiComboboxContext.js";
9
+ import { UiComboboxInput } from "../UiCombobox/UiComboboxInput.js";
10
+ import { UiComboboxListItem } from "../UiCombobox/UiComboboxListItem.js";
11
+ import { UiComboboxPopup } from "../UiCombobox/UiComboboxPopup.js";
12
+ import { useComboboxChrome } from "../UiCombobox/useComboboxChrome.js";
13
+ import { UiSectionHeading } from "../UiSectionHeading/UiSectionHeading.js";
14
+ import { useAsyncListSource } from "./useAsyncListSource.js";
15
+ const { b, e } = bem("gd-ui-kit-autocomplete");
16
+ const DEFAULT_DEBOUNCE_MS = 400;
17
+ // Per-instance, per-generation synthetic id for the Load-more row; activating
18
+ // it triggers a page fetch instead of `onSelect`. `listboxId` keeps it unique
19
+ // across autocompletes on one page; `generation` makes it differ once the
20
+ // query reloads, so a stale highlight can't carry onto the next query's row.
21
+ const loadMoreId = (listboxId, generation) => `${listboxId}-load-more-${generation}`;
22
+ // Stable default so an omitted `selectedIds` doesn't feed a fresh array into the
23
+ // memo deps every render.
24
+ const NO_SELECTED_IDS = [];
25
+ // Mirrors `useCombobox`'s key map. Space, Home and End are left to the input
26
+ // so the user can edit the query text natively.
27
+ const autocompleteKeys = makeKeyboardNavigation({
28
+ onArrowDown: [{ code: "ArrowDown" }],
29
+ onArrowUp: [{ code: "ArrowUp" }],
30
+ onEnter: [{ code: ["Enter", "NumpadEnter"] }],
31
+ onEscape: [{ code: "Escape" }],
32
+ });
33
+ /**
34
+ * Async autocomplete: text input + sectioned listbox driven by a `loadOptions`
35
+ * loader. Implements the ARIA combobox+listbox APG pattern.
36
+ *
37
+ * @internal
38
+ */
39
+ export function UiAutocomplete({ loadOptions, selectedIds = NO_SELECTED_IDS, onSelect, debounceMs = DEFAULT_DEBOUNCE_MS, messages, accessibilityConfig, dataTestId, }) {
40
+ const intl = useIntl();
41
+ const listboxId = useId();
42
+ const copy = useMemo(() => ({
43
+ searchPlaceholder: messages?.searchPlaceholder ?? intl.formatMessage(uiAutocompleteMessages.searchPlaceholder),
44
+ stateLoading: messages?.stateLoading ?? intl.formatMessage(uiAutocompleteMessages.stateLoading),
45
+ stateError: messages?.stateError ?? intl.formatMessage(uiAutocompleteMessages.stateError),
46
+ stateNoMatch: messages?.stateNoMatch ?? intl.formatMessage(uiAutocompleteMessages.stateNoMatch),
47
+ loadMore: messages?.loadMore ?? intl.formatMessage(uiAutocompleteMessages.loadMore),
48
+ }), [intl, messages]);
49
+ const source = useAsyncListSource(loadOptions, { debounceMs });
50
+ // The Load-more row's id is stamped with the load generation, so a highlight
51
+ // left on it before a query change can't re-attach to the next query's row
52
+ // (a constant id would survive and let Enter page the wrong query — the
53
+ // synthetic row is the one id the by-id highlight tracking can't otherwise
54
+ // tell apart across queries, since real options carry distinct ids).
55
+ const loadMoreRowId = loadMoreId(listboxId, source.generation);
56
+ const selectedIdSet = useMemo(() => new Set(selectedIds), [selectedIds]);
57
+ // Flat list of the rows the highlight can land on. Section headings and
58
+ // status rows render around it but never participate.
59
+ const focusableOptions = useMemo(() => {
60
+ const options = [];
61
+ for (const section of source.sections) {
62
+ for (const option of section.options) {
63
+ if (selectedIdSet.has(option.id)) {
64
+ continue;
65
+ }
66
+ options.push({ id: option.id, label: option.label });
67
+ }
68
+ }
69
+ if (source.hasNextPage && source.status !== "loadingMore") {
70
+ options.push({ id: loadMoreRowId, label: copy.loadMore });
71
+ }
72
+ return options;
73
+ }, [source.sections, source.hasNextPage, source.status, selectedIdSet, copy.loadMore, loadMoreRowId]);
74
+ // Chrome tracks the highlight by row id, so a row that disappears (results
75
+ // change, Load-more row removed while its page loads) drops the highlight
76
+ // on its own — no manual resets to keep in sync.
77
+ const chrome = useComboboxChrome(focusableOptions);
78
+ const activeOptionData = useMemo(() => {
79
+ if (!chrome.activeOption) {
80
+ return undefined;
81
+ }
82
+ if (chrome.activeOption.id === loadMoreRowId) {
83
+ return "loadMore";
84
+ }
85
+ return findOptionById(source.sections, chrome.activeOption.id);
86
+ }, [chrome.activeOption, source.sections, loadMoreRowId]);
87
+ const handleSelect = useCallback((target) => {
88
+ if (target === "loadMore") {
89
+ // Drop the highlight while the next page loads: the Load-more row
90
+ // disappears, and a real option highlighted beforehand (e.g. clicked
91
+ // Load-more without hovering it first) must not stay Enter-armed.
92
+ chrome.setActiveIndex(null);
93
+ source.loadMore();
94
+ return;
95
+ }
96
+ onSelect(target);
97
+ // Clear the input + re-prime the loader so the next open shows the
98
+ // unfiltered first page rather than the previous query's results.
99
+ source.reset();
100
+ chrome.setIsOpen(false);
101
+ }, [chrome, source, onSelect]);
102
+ // Reopening after a failed load re-runs it, so every open path (click, type,
103
+ // and the keyboard arrows below) routes through this first to recover.
104
+ const retryIfErrored = useCallback(() => {
105
+ if (!chrome.isOpen && source.status === "error") {
106
+ source.retry();
107
+ }
108
+ }, [chrome.isOpen, source]);
109
+ const onInputKeyDown = useMemo(() => {
110
+ const onEnter = chrome.isOpen && activeOptionData ? () => handleSelect(activeOptionData) : undefined;
111
+ // Leave Escape unhandled when idle so an enclosing modal can dismiss.
112
+ const hasSomethingToClose = chrome.isOpen || source.inputValue.length > 0;
113
+ const onEscape = hasSomethingToClose
114
+ ? (event) => {
115
+ // Stop the native event too so an enclosing UiModalDialog's
116
+ // document Escape listener doesn't dismiss the whole dialog.
117
+ event.nativeEvent.stopPropagation();
118
+ if (chrome.isOpen) {
119
+ chrome.setIsOpen(false);
120
+ return;
121
+ }
122
+ source.reset();
123
+ }
124
+ : undefined;
125
+ return autocompleteKeys({
126
+ onArrowDown: () => {
127
+ retryIfErrored();
128
+ chrome.focusByDelta(1);
129
+ },
130
+ onArrowUp: () => {
131
+ retryIfErrored();
132
+ chrome.focusByDelta(-1);
133
+ },
134
+ onEnter,
135
+ onEscape,
136
+ });
137
+ }, [chrome, source, activeOptionData, handleSelect, retryIfErrored]);
138
+ const selectOptionFromContext = useCallback((option) => {
139
+ if (option.id === loadMoreRowId) {
140
+ handleSelect("loadMore");
141
+ return;
142
+ }
143
+ const resolved = findOptionById(source.sections, option.id);
144
+ if (resolved) {
145
+ handleSelect(resolved);
146
+ }
147
+ }, [handleSelect, source.sections, loadMoreRowId]);
148
+ // Stay visible across loading / error / no-match so the status row reaches
149
+ // the user; the sync combobox's "hide when empty" default does not apply here.
150
+ const shouldRenderPopup = chrome.isOpen;
151
+ const setIsOpen = useCallback((open) => {
152
+ if (open) {
153
+ retryIfErrored();
154
+ }
155
+ chrome.setIsOpen(open);
156
+ }, [chrome, retryIfErrored]);
157
+ const state = useMemo(() => ({
158
+ inputValue: source.inputValue,
159
+ onInputChange: (next) => {
160
+ source.setInputValue(next);
161
+ setIsOpen(true);
162
+ },
163
+ onInputKeyDown,
164
+ onInputBlur: () => chrome.setIsOpen(false),
165
+ availableOptions: focusableOptions,
166
+ activeIndex: chrome.activeIndex,
167
+ setActiveIndex: chrome.setActiveIndex,
168
+ activeOption: chrome.activeOption,
169
+ selectedOption: undefined,
170
+ selectOption: selectOptionFromContext,
171
+ isOpen: chrome.isOpen,
172
+ setIsOpen,
173
+ shouldRenderPopup,
174
+ anchorRef: chrome.anchorRef,
175
+ registerItemRef: chrome.registerItemRef,
176
+ creatable: false,
177
+ listboxId,
178
+ }), [
179
+ chrome,
180
+ source,
181
+ setIsOpen,
182
+ focusableOptions,
183
+ shouldRenderPopup,
184
+ onInputKeyDown,
185
+ selectOptionFromContext,
186
+ listboxId,
187
+ ]);
188
+ const visibleSections = source.sections
189
+ .map((section) => ({
190
+ ...section,
191
+ options: section.options.filter((o) => !selectedIdSet.has(o.id)),
192
+ }))
193
+ .filter((section) => section.options.length > 0);
194
+ // "No matching options" must mean the search found nothing — not that every
195
+ // match was hidden because it's already selected (a successful search). Key
196
+ // it off the loader's own result, before the selected-id filter.
197
+ const loaderReturnedNothing = source.sections.every((section) => section.options.length === 0);
198
+ const showEmptyState = source.status === "idle" && loaderReturnedNothing && !source.hasNextPage;
199
+ return (_jsx("div", { className: b(), "data-testid": dataTestId, children: _jsxs(UiComboboxContextProvider, { state: state, children: [
200
+ _jsx(UiComboboxInput, { placeholder: copy.searchPlaceholder, accessibilityConfig: accessibilityConfig }), _jsx(UiComboboxPopup, { children: _jsxs("ul", { className: e("list"), role: "listbox", id: listboxId, tabIndex: -1, children: [source.status === "loading" ? _jsx(StatusRow, { text: copy.stateLoading }) : null, source.status === "error" ? _jsx(StatusRow, { text: copy.stateError }) : null, visibleSections.length > 0 ? (_jsx(Sections, { sections: visibleSections, focusableOptions: focusableOptions })) : null, source.hasNextPage && source.status !== "loadingMore" ? (_jsx(LoadMoreRow, { id: loadMoreRowId, index: focusableOptions.findIndex((o) => o.id === loadMoreRowId), label: copy.loadMore })) : null, source.status === "loadingMore" ? _jsx(StatusRow, { text: copy.stateLoading }) : null, showEmptyState ? _jsx(StatusRow, { text: copy.stateNoMatch }) : null] }) })
201
+ ] }) }));
202
+ }
203
+ // `index` on each item must be its position in `focusableOptions` — chrome's
204
+ // active-index navigation and item-ref registry are keyed off that.
205
+ function Sections({ sections, focusableOptions, }) {
206
+ return sections.map((section) => (_jsxs(Fragment, { children: [
207
+ _jsx("li", { role: "presentation", className: e("section-header"), children: _jsx(UiSectionHeading, { label: section.label }) }), section.options.map((option) => {
208
+ const index = focusableOptions.findIndex((o) => o.id === option.id);
209
+ if (index < 0) {
210
+ return null;
211
+ }
212
+ return (_jsxs(UiComboboxListItem, { option: { id: option.id, label: option.label }, index: index, className: e("option"), children: [
213
+ _jsx("span", { className: e("option-label"), children: option.label }), option.secondaryText ? (_jsx("span", { className: e("option-secondary"), children: option.secondaryText })) : null] }, option.id));
214
+ })] }, section.id)));
215
+ }
216
+ function StatusRow({ text }) {
217
+ return (_jsx("li", { role: "presentation", className: e("state"), children: text }));
218
+ }
219
+ function LoadMoreRow({ id, index, label }) {
220
+ return (_jsx(UiComboboxListItem, { option: { id, label }, index: index, className: e("load-more"), children: _jsx("span", { children: label }) }));
221
+ }
222
+ function findOptionById(sections, id) {
223
+ for (const section of sections) {
224
+ const found = section.options.find((o) => o.id === id);
225
+ if (found) {
226
+ return found;
227
+ }
228
+ }
229
+ return undefined;
230
+ }
@@ -0,0 +1,61 @@
1
+ import { type IAccessibilityConfigBase } from "../../typings/accessibility.js";
2
+ /**
3
+ * Minimal option shape; specialized pickers map their domain objects onto it.
4
+ *
5
+ * @internal
6
+ */
7
+ export interface IUiAutocompleteOption {
8
+ id: string;
9
+ label: string;
10
+ /** Secondary label rendered muted next to the primary label. */
11
+ secondaryText?: string;
12
+ }
13
+ /** @internal */
14
+ export interface IUiAutocompleteSection<T extends IUiAutocompleteOption = IUiAutocompleteOption> {
15
+ id: string;
16
+ label: string;
17
+ options: T[];
18
+ }
19
+ /**
20
+ * User-facing copy overrides. Omitted fields fall back to the kit default.
21
+ *
22
+ * @internal
23
+ */
24
+ export interface IUiAutocompleteMessages {
25
+ searchPlaceholder?: string;
26
+ stateLoading?: string;
27
+ stateError?: string;
28
+ stateNoMatch?: string;
29
+ loadMore?: string;
30
+ }
31
+ /** @internal */
32
+ export interface IUiAutocompleteLoadResult<T extends IUiAutocompleteOption = IUiAutocompleteOption> {
33
+ sections: IUiAutocompleteSection<T>[];
34
+ /** Show a "Load more" row whose activation calls the loader with the next page. */
35
+ hasNextPage?: boolean;
36
+ }
37
+ /** @internal */
38
+ export interface IUiAutocompleteProps<T extends IUiAutocompleteOption = IUiAutocompleteOption> {
39
+ /**
40
+ * Called with the debounced search and zero-based `page` index. Page 0
41
+ * fires on every query change; subsequent pages fire on Load-more.
42
+ *
43
+ * Must be referentially stable (memoize with `useCallback`) — it is a
44
+ * fetch dependency, so a new identity each render re-runs the page-0 query.
45
+ */
46
+ loadOptions: (search: string, page: number) => Promise<IUiAutocompleteLoadResult<T>>;
47
+ /** Filtered out of the dropdown so the user cannot pick the same option twice. */
48
+ selectedIds?: ReadonlyArray<string>;
49
+ onSelect: (option: T) => void;
50
+ /** Debounce delay for the input → loader path. Defaults to 400 ms. */
51
+ debounceMs?: number;
52
+ messages?: IUiAutocompleteMessages;
53
+ /**
54
+ * Forwarded to the search input for the accessible name / description.
55
+ * The combobox-internal attributes (`role`, `ariaExpanded`, `ariaControls`,
56
+ * `ariaAutocomplete`) are owned by the component and cannot be overridden.
57
+ */
58
+ accessibilityConfig?: IAccessibilityConfigBase;
59
+ dataTestId?: string;
60
+ }
61
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/@ui/UiAutocomplete/types.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAE/E;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,gBAAgB;AAChB,MAAM,WAAW,sBAAsB,CAAC,CAAC,SAAS,qBAAqB,GAAG,qBAAqB;IAC3F,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,CAAC,EAAE,CAAC;CAChB;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACpC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,gBAAgB;AAChB,MAAM,WAAW,yBAAyB,CAAC,CAAC,SAAS,qBAAqB,GAAG,qBAAqB;IAC9F,QAAQ,EAAE,sBAAsB,CAAC,CAAC,CAAC,EAAE,CAAC;IACtC,mFAAmF;IACnF,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,gBAAgB;AAChB,MAAM,WAAW,oBAAoB,CAAC,CAAC,SAAS,qBAAqB,GAAG,qBAAqB;IACzF;;;;;;OAMG;IACH,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,yBAAyB,CAAC,CAAC,CAAC,CAAC,CAAC;IACrF,kFAAkF;IAClF,WAAW,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACpC,QAAQ,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,IAAI,CAAC;IAC9B,wEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,uBAAuB,CAAC;IACnC;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,wBAAwB,CAAC;IAC/C,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB"}
@@ -0,0 +1,2 @@
1
+ // (C) 2026 GoodData Corporation
2
+ export {};