@gooddata/sdk-ui-kit 11.51.0-alpha.1 → 11.51.0-alpha.3

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 (55) hide show
  1. package/esm/@ui/UiGeneralAccessRadio/UiGeneralAccessRadio.d.ts +3 -2
  2. package/esm/@ui/UiGeneralAccessRadio/UiGeneralAccessRadio.d.ts.map +1 -1
  3. package/esm/@ui/UiGeneralAccessRadio/UiGeneralAccessRadio.js +7 -3
  4. package/esm/@ui/UiGranteeAsyncPicker/UiGranteeAsyncPicker.d.ts +5 -0
  5. package/esm/@ui/UiGranteeAsyncPicker/UiGranteeAsyncPicker.d.ts.map +1 -1
  6. package/esm/@ui/UiGranteeAsyncPicker/UiGranteeAsyncPicker.js +3 -5
  7. package/esm/@ui/UiGranteeRowControls/UiGranteeRowControls.d.ts +10 -16
  8. package/esm/@ui/UiGranteeRowControls/UiGranteeRowControls.d.ts.map +1 -1
  9. package/esm/@ui/UiGranteeRowControls/UiGranteeRowControls.js +19 -32
  10. package/esm/@ui/UiLabelsChecklist/UiLabelsChecklist.d.ts +3 -1
  11. package/esm/@ui/UiLabelsChecklist/UiLabelsChecklist.d.ts.map +1 -1
  12. package/esm/@ui/UiLabelsChecklist/UiLabelsChecklist.js +12 -3
  13. package/esm/@ui/UiObjectShareDialog/UiObjectShareDialogCard.d.ts +9 -3
  14. package/esm/@ui/UiObjectShareDialog/UiObjectShareDialogCard.d.ts.map +1 -1
  15. package/esm/@ui/UiObjectShareDialog/UiObjectShareDialogCard.js +3 -3
  16. package/esm/@ui/UiPermissionMenu/UiPermissionMenu.d.ts +18 -10
  17. package/esm/@ui/UiPermissionMenu/UiPermissionMenu.d.ts.map +1 -1
  18. package/esm/@ui/UiPermissionMenu/UiPermissionMenu.js +45 -13
  19. package/esm/@ui/UiSubmenuHeader/UiSubmenuHeader.d.ts +4 -2
  20. package/esm/@ui/UiSubmenuHeader/UiSubmenuHeader.d.ts.map +1 -1
  21. package/esm/@ui/UiSubmenuHeader/UiSubmenuHeader.js +2 -2
  22. package/esm/Menu/positioning/PositionedMenuContent.d.ts.map +1 -1
  23. package/esm/Menu/positioning/PositionedMenuContent.js +10 -1
  24. package/esm/Overlay/Overlay.d.ts.map +1 -1
  25. package/esm/Overlay/Overlay.js +5 -1
  26. package/esm/ParameterControl/StringParameterControlDropdown.js +2 -2
  27. package/esm/RichText/RichTextWithTooltip.d.ts.map +1 -1
  28. package/esm/RichText/RichTextWithTooltip.js +3 -0
  29. package/esm/RichText/hooks/useEvaluatedMetricsAndAttributes.js +1 -1
  30. package/esm/Timezone/TimezoneSelect.d.ts +108 -0
  31. package/esm/Timezone/TimezoneSelect.d.ts.map +1 -0
  32. package/esm/Timezone/TimezoneSelect.js +90 -0
  33. package/esm/Timezone/timezones.d.ts +86 -0
  34. package/esm/Timezone/timezones.d.ts.map +1 -0
  35. package/esm/Timezone/timezones.js +253 -0
  36. package/esm/index.d.ts +3 -2
  37. package/esm/index.d.ts.map +1 -1
  38. package/esm/index.js +3 -2
  39. package/esm/locales.d.ts +9 -6
  40. package/esm/locales.d.ts.map +1 -1
  41. package/esm/locales.js +4 -3
  42. package/esm/sdk-ui-kit.d.ts +238 -61
  43. package/package.json +11 -11
  44. package/src/@ui/UiGranteeRowControls/UiGranteeRowControls.scss +0 -8
  45. package/src/@ui/UiLabelsChecklist/UiLabelsChecklist.scss +2 -2
  46. package/src/@ui/UiPermissionMenu/UiPermissionMenu.scss +5 -0
  47. package/src/@ui/index.scss +0 -1
  48. package/src/Timezone/TimezoneSelect.scss +22 -0
  49. package/styles/css/main.css +17 -12
  50. package/styles/css/main.css.map +1 -1
  51. package/styles/scss/main.scss +1 -0
  52. package/esm/@ui/UiMoreOptionsMenu/UiMoreOptionsMenu.d.ts +0 -28
  53. package/esm/@ui/UiMoreOptionsMenu/UiMoreOptionsMenu.d.ts.map +0 -1
  54. package/esm/@ui/UiMoreOptionsMenu/UiMoreOptionsMenu.js +0 -85
  55. package/src/@ui/UiMoreOptionsMenu/UiMoreOptionsMenu.scss +0 -10
@@ -1 +1 @@
1
- {"version":3,"file":"PositionedMenuContent.d.ts","sourceRoot":"","sources":["../../../src/Menu/positioning/PositionedMenuContent.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAA4C,MAAM,OAAO,CAAC;AAEjF,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AASjE,MAAM,WAAW,2BAA4B,SAAQ,mBAAmB;IACpE,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,WAAW,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,SAAS,CAAC;CACvB;AAED,MAAM,WAAW,2BAA2B;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,2BAA2B,2CAqEvE"}
1
+ {"version":3,"file":"PositionedMenuContent.d.ts","sourceRoot":"","sources":["../../../src/Menu/positioning/PositionedMenuContent.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAA4C,MAAM,OAAO,CAAC;AAMjF,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AASjE,MAAM,WAAW,2BAA4B,SAAQ,mBAAmB;IACpE,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,WAAW,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,SAAS,CAAC;CACvB;AAED,MAAM,WAAW,2BAA2B;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,2BAA2B,2CA+EvE"}
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  // (C) 2007-2026 GoodData Corporation
3
3
  import { useCallback, useEffect, useRef, useState } from "react";
4
+ import { FLOATING_ELEMENT_DATA_ATTR, useRegisterFloatingAnchor, } from "../../@ui/hooks/useCloseOnOutsideClick.js";
4
5
  import { calculateMenuPosition, getElementDimensions, getElementDimensionsAndCoords, getViewportDimensionsAndCoords, } from "./positioningCalculations.js";
5
6
  export function PositionedMenuContent(props) {
6
7
  const [state, setState] = useState({
@@ -8,6 +9,14 @@ export function PositionedMenuContent(props) {
8
9
  top: 0,
9
10
  });
10
11
  const menuElRef = useRef(null);
12
+ // The menu portals to <body>, outside whatever opened it, so overlay/dialog outside-click
13
+ // detection cannot attribute its clicks by DOM containment. Mark it as a floating element and
14
+ // register the toggler as its anchor, so ownership resolves through the anchor chain (same
15
+ // contract as UiFloatingElement).
16
+ const assignMenuEl = useCallback((node) => {
17
+ menuElRef.current = node;
18
+ }, []);
19
+ const setMenuElWithAnchorRegistry = useRegisterFloatingAnchor(assignMenuEl, props.togglerEl);
11
20
  const positionMenu = useCallback(() => {
12
21
  if (!props.togglerEl || !menuElRef.current) {
13
22
  return;
@@ -53,5 +62,5 @@ export function PositionedMenuContent(props) {
53
62
  position: "absolute",
54
63
  left: state.left,
55
64
  top: state.top,
56
- }, ref: menuElRef, children: props.children }));
65
+ }, ref: setMenuElWithAnchorRegistry, [FLOATING_ELEMENT_DATA_ATTR]: true, children: props.children }));
57
66
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Overlay.d.ts","sourceRoot":"","sources":["../../src/Overlay/Overlay.tsx"],"names":[],"mappings":"AAEA,OAAO,EACH,KAAK,aAAa,EAClB,SAAS,EACT,KAAK,WAAW,EAInB,MAAM,OAAO,CAAC;AAgBf,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAuBtE,eAAO,MAAM,uBAAuB,iBAAiB,CAAC;AAqCtD;;GAEG;AACH,qBAAa,OAAO,CAAC,CAAC,GAAG,WAAW,CAAE,SAAQ,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,aAAa,CAAC;IACpF,OAAc,YAAY,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAsBrD;IAEF,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAS;IAC5B,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,YAAY,CAAgC;IACpD,OAAO,CAAC,aAAa,CAEb;IACR,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,kBAAkB,CAAU;IACpC,OAAO,CAAC,aAAa,CAAU;IAC/B,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAgB,WAAW,0FAAkB;IACrC,OAAO,EAAE,WAAW,CAAC,OAAO,cAAc,CAAC,CAAC;IACpD,OAAO,CAAC,QAAQ,CAA6B;IAE7C,YAAY,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EA6BlC;IAEe,yBAAyB,IAAI,IAAI,CAGhD;IAEe,iBAAiB,IAAI,IAAI,CAaxC;IAEe,gCAAgC,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,IAAI,CAKlF;IAEe,qBAAqB,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,aAAa,GAAG,OAAO,CAOpG;IAEe,kBAAkB,IAAI,IAAI,CAEzC;IAEe,oBAAoB,IAAI,IAAI,CAgB3C;IAEe,MAAM,4CA+BrB;IAEM,KAAK,aAyCV;IAEF,OAAO,CAAC,6BAA6B,CAyDnC;IAEF,OAAO,CAAC,qBAAqB,CAI3B;IAEF,OAAO,CAAC,gBAAgB,CAMtB;IAEF,OAAO,CAAC,WAAW,CAIjB;IAEF,SAAS,CAAC,SAAS,IAAI,MAAM,GAAG,SAAS,CAExC;IAEM,gBAAgB,sBA4BrB;IAEF,OAAO,CAAC,iBAAiB,CAIvB;IAEF;;;OAGG;IACH,OAAO,CAAC,eAAe,CAOrB;IAEF,OAAO,CAAC,kBAAkB,CAMxB;IAEF,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,mCAAmC;IAS3C,OAAO,CAAC,sBAAsB,CAI5B;IAEF,OAAO,CAAC,eAAe,CAIrB;IAEF,OAAO,CAAC,kBAAkB,CAiBxB;IAEF,OAAO,CAAC,uBAAuB,CAqB7B;IAEF,OAAO,CAAC,SAAS,CAEf;IAGF,OAAO,CAAC,uBAAuB,CAK7B;IAEK,mBAAmB,CAAC,CAAC,EAAE,UAAU,GAAG,IAAI,CAQ9C;IAEM,mBAAmB,CAAC,CAAC,EAAE,UAAU,GAAG,IAAI,CAQ9C;IAEM,gBAAgB,aAMrB;IAEK,mBAAmB,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI,CAIzC;IAEM,aAAa,CAAC,CAAC,EAAE,aAAa,GAAG,IAAI,CAQ3C;IAED,OAAO,CAAC,eAAe,CAUrB;IAEF,OAAO,CAAC,YAAY,CAElB;IAEF,OAAO,CAAC,eAAe,CAErB;IAEF,OAAO,CAAC,UAAU,CAahB;IAEF,OAAO,CAAC,oBAAoB;IAmB5B,OAAO,CAAC,qBAAqB;CAOhC"}
1
+ {"version":3,"file":"Overlay.d.ts","sourceRoot":"","sources":["../../src/Overlay/Overlay.tsx"],"names":[],"mappings":"AAEA,OAAO,EACH,KAAK,aAAa,EAClB,SAAS,EACT,KAAK,WAAW,EAInB,MAAM,OAAO,CAAC;AAgBf,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAuBtE,eAAO,MAAM,uBAAuB,iBAAiB,CAAC;AAqCtD;;GAEG;AACH,qBAAa,OAAO,CAAC,CAAC,GAAG,WAAW,CAAE,SAAQ,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,aAAa,CAAC;IACpF,OAAc,YAAY,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAsBrD;IAEF,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAS;IAC5B,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,YAAY,CAAgC;IACpD,OAAO,CAAC,aAAa,CAEb;IACR,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,kBAAkB,CAAU;IACpC,OAAO,CAAC,aAAa,CAAU;IAC/B,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAgB,WAAW,0FAAkB;IACrC,OAAO,EAAE,WAAW,CAAC,OAAO,cAAc,CAAC,CAAC;IACpD,OAAO,CAAC,QAAQ,CAA6B;IAE7C,YAAY,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EA6BlC;IAEe,yBAAyB,IAAI,IAAI,CAGhD;IAEe,iBAAiB,IAAI,IAAI,CAaxC;IAEe,gCAAgC,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,IAAI,CAKlF;IAEe,qBAAqB,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,aAAa,GAAG,OAAO,CAOpG;IAEe,kBAAkB,IAAI,IAAI,CAEzC;IAEe,oBAAoB,IAAI,IAAI,CAgB3C;IAEe,MAAM,4CA+BrB;IAEM,KAAK,aAyCV;IAEF,OAAO,CAAC,6BAA6B,CAyDnC;IAEF,OAAO,CAAC,qBAAqB,CAI3B;IAEF,OAAO,CAAC,gBAAgB,CAMtB;IAEF,OAAO,CAAC,WAAW,CAIjB;IAEF,SAAS,CAAC,SAAS,IAAI,MAAM,GAAG,SAAS,CAExC;IAEM,gBAAgB,sBA4BrB;IAEF,OAAO,CAAC,iBAAiB,CAIvB;IAEF;;;OAGG;IACH,OAAO,CAAC,eAAe,CAOrB;IAEF,OAAO,CAAC,kBAAkB,CAMxB;IAEF,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,mCAAmC;IAS3C,OAAO,CAAC,sBAAsB,CAI5B;IAEF,OAAO,CAAC,eAAe,CAIrB;IAEF,OAAO,CAAC,kBAAkB,CAiBxB;IAEF,OAAO,CAAC,uBAAuB,CAqB7B;IAEF,OAAO,CAAC,SAAS,CAEf;IAGF,OAAO,CAAC,uBAAuB,CAS7B;IAEK,mBAAmB,CAAC,CAAC,EAAE,UAAU,GAAG,IAAI,CAQ9C;IAEM,mBAAmB,CAAC,CAAC,EAAE,UAAU,GAAG,IAAI,CAQ9C;IAEM,gBAAgB,aAMrB;IAEK,mBAAmB,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI,CAIzC;IAEM,aAAa,CAAC,CAAC,EAAE,aAAa,GAAG,IAAI,CAQ3C;IAED,OAAO,CAAC,eAAe,CAUrB;IAEF,OAAO,CAAC,YAAY,CAElB;IAEF,OAAO,CAAC,eAAe,CAErB;IAEF,OAAO,CAAC,UAAU,CAahB;IAEF,OAAO,CAAC,oBAAoB;IAmB5B,OAAO,CAAC,qBAAqB;CAOhC"}
@@ -370,7 +370,11 @@ export class Overlay extends Component {
370
370
  isElementInChildOverlay = (element) => {
371
371
  const overlays = Array.from(document.querySelectorAll(".overlay-wrapper"));
372
372
  const thisOverlayIndex = overlays.findIndex((overlay) => overlay === this.overlayRef.current);
373
- return overlays.slice(thisOverlayIndex + 1).some((overlay) => overlay.contains(element));
373
+ // floating-ui panels portal to <body>, outside every .overlay-wrapper, so plain containment
374
+ // misses a popup anchored inside a child overlay — attribute it through its anchor chain
375
+ return overlays
376
+ .slice(thisOverlayIndex + 1)
377
+ .some((overlay) => isClickInsideOwnSubtree(element, overlay));
374
378
  };
375
379
  onDocumentMouseDown(e) {
376
380
  const target = e.target instanceof Element ? e.target : null;
@@ -22,10 +22,10 @@ export function StringParameterControlDropdown({ name, value, resetValue, constr
22
22
  return (_jsx(ParameterControlDropdown, { name: name, draft: draft, onDraftChange: setDraft, inputType: "text", inputId: inputId, ariaAttributes: ariaAttributes, errorMessage: error ? (_jsx(FormattedMessage, { ...error, values: { minLength: constraints?.minLength, maxLength: constraints?.maxLength } })) : undefined, onReset: showReset ? () => setDraft(resetValue) : undefined, onApply: () => onApply(draft), onCancel: onCancel }));
23
23
  }
24
24
  function getStringDraftValidationError(draft, constraints) {
25
- if (isValidStringParameterValue(draft, constraints)) {
25
+ const { minLength, maxLength } = constraints ?? {};
26
+ if (isValidStringParameterValue(draft, { minLength, maxLength })) {
26
27
  return undefined;
27
28
  }
28
- const { minLength } = constraints ?? {};
29
29
  return minLength !== undefined && draft.length < minLength
30
30
  ? messages.errorTooShort
31
31
  : messages.errorTooLong;
@@ -1 +1 @@
1
- {"version":3,"file":"RichTextWithTooltip.d.ts","sourceRoot":"","sources":["../../src/RichText/RichTextWithTooltip.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,YAAY,EAAW,MAAM,OAAO,CAAC;AAQnD,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAE7D,OAAO,EAAE,KAAK,cAAc,EAAY,MAAM,eAAe,CAAC;AAoB9D;;GAEG;AACH,MAAM,WAAW,yBAA0B,SAAQ,cAAc;IAC7D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kBAAkB,CAAC,EAAE,WAAW,EAAE,CAAC;IACnC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,eAAe,CAAC,EAAE,YAAY,CAAC;CAClC;AA4FD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,yBAAyB,2CAMnE"}
1
+ {"version":3,"file":"RichTextWithTooltip.d.ts","sourceRoot":"","sources":["../../src/RichText/RichTextWithTooltip.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,YAAY,EAAW,MAAM,OAAO,CAAC;AAQnD,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAE7D,OAAO,EAAE,KAAK,cAAc,EAAY,MAAM,eAAe,CAAC;AAoB9D;;GAEG;AACH,MAAM,WAAW,yBAA0B,SAAQ,cAAc;IAC7D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kBAAkB,CAAC,EAAE,WAAW,EAAE,CAAC;IACnC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,eAAe,CAAC,EAAE,YAAY,CAAC;CAClC;AA+FD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,yBAAyB,2CAMnE"}
@@ -26,6 +26,8 @@ function RichTextWithTooltipCore({ value, onChange, renderMode, editPlaceholder,
26
26
  return (_jsx(RichText, { value: value, onChange: onChange, renderMode: renderMode, editPlaceholder: editPlaceholder, editRows: editRows, emptyElement: emptyElement, className: className, autoResize: autoResize, referencesEnabled: referencesEnabled, filters: filters, separators: separators, LoadingComponent: LoadingComponent, onLoadingChanged: onLoadingChanged, onError: onError, rawContent: rawContent, execConfig: {
27
27
  timestamp: execConfig?.timestamp,
28
28
  dataSamplingPercentage: execConfig?.dataSamplingPercentage,
29
+ // omit when undefined so it does not bust the execution's defFingerprint
30
+ ...(execConfig?.timezone ? { timezone: execConfig.timezone } : {}),
29
31
  } }));
30
32
  }, [
31
33
  value,
@@ -45,6 +47,7 @@ function RichTextWithTooltipCore({ value, onChange, renderMode, editPlaceholder,
45
47
  separators,
46
48
  execConfig?.timestamp,
47
49
  execConfig?.dataSamplingPercentage,
50
+ execConfig?.timezone,
48
51
  ]);
49
52
  if (!showTooltip) {
50
53
  return Component;
@@ -16,7 +16,7 @@ export function useEvaluatedMetricsAndAttributes(references, filters, config) {
16
16
  .forItems(items, filters)
17
17
  .withExecConfig(execConfig)
18
18
  : undefined,
19
- }, [execConfig.timestamp, execConfig.dataSamplingPercentage, isFiltersLoading]);
19
+ }, [execConfig.timestamp, execConfig.dataSamplingPercentage, execConfig.timezone, isFiltersLoading]);
20
20
  const { status: loadStatus, result: loadResult, error: loadError, } = useCancelablePromise({
21
21
  promise: async () => {
22
22
  if (!executionResult) {
@@ -0,0 +1,108 @@
1
+ import { type ReactElement, type ReactNode } from "react";
2
+ import { type IUiListboxItem } from "../@ui/UiListbox/types.js";
3
+ import { type IDropdownButtonRenderProps } from "../Dropdown/Dropdown.js";
4
+ /**
5
+ * Special item rendered above the divider in {@link TimezoneSelect}, ahead of the curated
6
+ * timezone list. Callers use these for entries like "Default (workspace)" or "From browser (...)".
7
+ *
8
+ * @internal
9
+ */
10
+ export interface ITimezoneSelectSpecialItem {
11
+ /**
12
+ * Value emitted by onChange when this item is selected. Undefined typically represents
13
+ * "no explicit timezone" (e.g. inherit the workspace setting).
14
+ */
15
+ id: string | undefined;
16
+ /**
17
+ * Display label of the item.
18
+ */
19
+ label: string;
20
+ }
21
+ /**
22
+ * Render props passed to a custom trigger renderer of {@link TimezoneSelect}.
23
+ *
24
+ * Extends the {@link IDropdownButtonRenderProps} contract of the underlying dropdown
25
+ * (toggle/open/close callbacks, isOpen, buttonRef and accessibility props) with the
26
+ * current selection so that custom triggers can display it.
27
+ *
28
+ * @internal
29
+ */
30
+ export interface ITimezoneSelectButtonRenderProps extends IDropdownButtonRenderProps {
31
+ /**
32
+ * Human-readable label of the current selection: the matching special item label,
33
+ * the selected timezone as "name (offset)" (e.g. "Prague (GMT+01:00)"),
34
+ * or the placeholder when nothing is selected.
35
+ */
36
+ buttonLabel: string;
37
+ /**
38
+ * Currently selected timezone ID (IANA ID, a special item ID or undefined).
39
+ */
40
+ value: string | undefined;
41
+ /**
42
+ * Whether the select is disabled. Custom triggers should reflect this state.
43
+ */
44
+ isDisabled?: boolean;
45
+ }
46
+ /**
47
+ * All label props are required so that callers pass localized strings — the component
48
+ * has no built-in English defaults to leak into the UI.
49
+ *
50
+ * @internal
51
+ */
52
+ export interface ITimezoneSelectProps {
53
+ /**
54
+ * Currently selected timezone ID (IANA ID, a special item ID or undefined).
55
+ */
56
+ value?: string;
57
+ /**
58
+ * Called when the user selects a timezone.
59
+ */
60
+ onChange: (id: string | undefined) => void;
61
+ /**
62
+ * Items rendered above the divider, ahead of the curated timezone list.
63
+ */
64
+ specialItems?: ITimezoneSelectSpecialItem[];
65
+ /**
66
+ * Label of the dropdown button when nothing is selected and no special item matches.
67
+ */
68
+ placeholder?: string;
69
+ /**
70
+ * Placeholder of the search input.
71
+ */
72
+ searchPlaceholder: string;
73
+ /**
74
+ * Accessibility label of the search input and the listbox.
75
+ */
76
+ ariaLabel: string;
77
+ /**
78
+ * Label shown when the search yields no results.
79
+ */
80
+ noMatchLabel: string;
81
+ isDisabled?: boolean;
82
+ /**
83
+ * Optional content rendered above the search input (e.g. a dialog-like header).
84
+ */
85
+ header?: ReactNode;
86
+ /**
87
+ * Custom renderer of the dropdown trigger. When not provided, a standard dropdown
88
+ * button showing the current selection is rendered.
89
+ *
90
+ * Use this to embed the select in surfaces where the default button does not fit,
91
+ * e.g. as an item of an options menu. The renderer receives the dropdown render props
92
+ * ({@link IDropdownButtonRenderProps}: toggleDropdown, isOpen, buttonRef, accessibility
93
+ * props, ...) together with the current selection ({@link ITimezoneSelectButtonRenderProps}).
94
+ */
95
+ renderButton?: (props: ITimezoneSelectButtonRenderProps) => ReactNode;
96
+ }
97
+ type TimezoneListItemData = string | undefined;
98
+ export declare function buildListboxItems(specialItems: ITimezoneSelectSpecialItem[], searchString: string): IUiListboxItem<TimezoneListItemData>[];
99
+ export declare function getSelectedItemId(value: string | undefined, specialItems: ITimezoneSelectSpecialItem[]): string | undefined;
100
+ /**
101
+ * Searchable dropdown for picking a timezone from the curated list, with optional special
102
+ * items (e.g. "Default (workspace)", "From browser (...)") rendered above a divider.
103
+ *
104
+ * @internal
105
+ */
106
+ export declare function TimezoneSelect({ value, onChange, specialItems, placeholder, searchPlaceholder, ariaLabel, noMatchLabel, isDisabled, header, renderButton }: ITimezoneSelectProps): ReactElement;
107
+ export {};
108
+ //# sourceMappingURL=TimezoneSelect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TimezoneSelect.d.ts","sourceRoot":"","sources":["../../src/Timezone/TimezoneSelect.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,SAAS,EAAqB,MAAM,OAAO,CAAC;AAG7E,OAAO,EAAkC,KAAK,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAEhG,OAAO,EAAY,KAAK,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AAYpF;;;;;GAKG;AACH,MAAM,WAAW,0BAA0B;IACvC;;;OAGG;IACH,EAAE,EAAE,MAAM,GAAG,SAAS,CAAC;IAEvB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,gCAAiC,SAAQ,0BAA0B;IAChF;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAE1B;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB;IACjC;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IAE3C;;OAEG;IACH,YAAY,CAAC,EAAE,0BAA0B,EAAE,CAAC;IAE5C;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,iBAAiB,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;OAEG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;IAEnB;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,gCAAgC,KAAK,SAAS,CAAC;CACzE;AAED,KAAK,oBAAoB,GAAG,MAAM,GAAG,SAAS,CAAC;AAM/C,wBAAgB,iBAAiB,CAC7B,YAAY,EAAE,0BAA0B,EAAE,EAC1C,YAAY,EAAE,MAAM,GACrB,cAAc,CAAC,oBAAoB,CAAC,EAAE,CA6BxC;AAGD,wBAAgB,iBAAiB,CAC7B,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,YAAY,EAAE,0BAA0B,EAAE,GAC3C,MAAM,GAAG,SAAS,CAMpB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,EAC3B,KAAK,EACL,QAAQ,EACR,YAAiB,EACjB,WAAgB,EAChB,iBAAiB,EACjB,SAAS,EACT,YAAY,EACZ,UAAU,EACV,MAAM,EACN,YAAY,EACf,EAAE,oBAAoB,GAAG,YAAY,CA2FrC"}
@@ -0,0 +1,90 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // (C) 2026 GoodData Corporation
3
+ import { useMemo, useState } from "react";
4
+ import { UiButton } from "../@ui/UiButton/UiButton.js";
5
+ import { UiListbox } from "../@ui/UiListbox/UiListbox.js";
6
+ import { Dropdown } from "../Dropdown/Dropdown.js";
7
+ import { Input } from "../Form/Input.js";
8
+ import { NoData } from "../NoData/NoData.js";
9
+ import { getTimezoneDisplayLabel, getTimezones, timezoneMatchesSearch } from "./timezones.js";
10
+ const DROPDOWN_ALIGN_POINTS = [{ align: "bl tl" }, { align: "tl bl" }];
11
+ const DROPDOWN_BODY_WIDTH = 300;
12
+ // ~10 rows of 28px compact list items visible before the list scrolls
13
+ const LIST_MAX_HEIGHT = 280;
14
+ // special items may carry an undefined id, so listbox entries need a synthetic unique id
15
+ const SPECIAL_ITEM_ID_PREFIX = "special-item-";
16
+ // exported for tests
17
+ export function buildListboxItems(specialItems, searchString) {
18
+ const search = searchString.trim().toLowerCase();
19
+ const specialListItems = specialItems
20
+ // keep the pre-filter index — getSelectedItemId derives the selected id from the
21
+ // unfiltered array, so filtered-out items must not shift the ids of the remaining ones
22
+ .map((item, index) => ({ item, index }))
23
+ .filter(({ item }) => !search || item.label.toLowerCase().includes(search))
24
+ .map(({ item, index }) => ({
25
+ type: "interactive",
26
+ id: `${SPECIAL_ITEM_ID_PREFIX}${index}`,
27
+ stringTitle: item.label,
28
+ data: item.id,
29
+ }));
30
+ const timezoneListItems = getTimezones()
31
+ .filter((timezone) => timezoneMatchesSearch(timezone, search))
32
+ .map((timezone) => ({
33
+ type: "interactive",
34
+ id: timezone.id,
35
+ stringTitle: getTimezoneDisplayLabel(timezone.id),
36
+ // the label shows the friendly name; surface the IANA ID on hover
37
+ tooltip: timezone.id,
38
+ data: timezone.id,
39
+ }));
40
+ return specialListItems.length > 0 && timezoneListItems.length > 0
41
+ ? [...specialListItems, { type: "separator" }, ...timezoneListItems]
42
+ : [...specialListItems, ...timezoneListItems];
43
+ }
44
+ // exported for tests
45
+ export function getSelectedItemId(value, specialItems) {
46
+ const specialIndex = specialItems.findIndex((item) => item.id === value);
47
+ if (specialIndex >= 0) {
48
+ return `${SPECIAL_ITEM_ID_PREFIX}${specialIndex}`;
49
+ }
50
+ return value;
51
+ }
52
+ /**
53
+ * Searchable dropdown for picking a timezone from the curated list, with optional special
54
+ * items (e.g. "Default (workspace)", "From browser (...)") rendered above a divider.
55
+ *
56
+ * @internal
57
+ */
58
+ export function TimezoneSelect({ value, onChange, specialItems = [], placeholder = "", searchPlaceholder, ariaLabel, noMatchLabel, isDisabled, header, renderButton, }) {
59
+ const [searchString, setSearchString] = useState("");
60
+ const selectedSpecialItem = specialItems.find((item) => item.id === value);
61
+ const buttonLabel = selectedSpecialItem
62
+ ? selectedSpecialItem.label
63
+ : value === undefined
64
+ ? placeholder
65
+ : getTimezoneDisplayLabel(value);
66
+ const items = useMemo(() => buildListboxItems(specialItems, searchString), [specialItems, searchString]);
67
+ const hasNoMatchingData = items.length === 0;
68
+ return (_jsx(Dropdown, { closeOnParentScroll: true, alignPoints: DROPDOWN_ALIGN_POINTS, className: "gd-timezone-select s-timezone-select", onOpenStateChanged: (isOpen) => {
69
+ if (!isOpen) {
70
+ setSearchString("");
71
+ }
72
+ }, renderButton: (renderProps) => renderButton ? (renderButton({ ...renderProps, buttonLabel, value, isDisabled })) : (_jsx(UiButton, { label: buttonLabel, onClick: renderProps.toggleDropdown, isDisabled: isDisabled, iconAfter: renderProps.isOpen ? "navigateUp" : "navigateDown", size: "small", dataTestId: "s-timezone-select-button", accessibilityConfig: {
73
+ ariaLabel,
74
+ ariaExpanded: renderProps.isOpen,
75
+ } })), renderBody: ({ closeDropdown, ariaAttributes }) => (_jsxs("div", { className: "gd-timezone-select__body s-timezone-select-body", children: [header, _jsx("div", { className: "gd-timezone-select__search s-timezone-select-search", children: _jsx(Input, { value: searchString, onChange: (newValue) => setSearchString(newValue.toString()), placeholder: searchPlaceholder, autofocus: true, clearOnEsc: true, isSearch: true, isSmall: true, onEscKeyPress: (event) => {
76
+ if (searchString) {
77
+ event.stopPropagation();
78
+ }
79
+ else {
80
+ closeDropdown();
81
+ }
82
+ }, accessibilityConfig: {
83
+ ariaLabel,
84
+ } }) }), hasNoMatchingData ? (_jsx(NoData, { hasNoMatchingData: true, notFoundLabel: noMatchLabel, noDataLabel: noMatchLabel })) : (_jsx(UiListbox
85
+ // remount when the filter changes so the focused index cannot point
86
+ // past the end of a shortened result list, which would break keyboard selection
87
+ , { width: DROPDOWN_BODY_WIDTH, maxHeight: LIST_MAX_HEIGHT, items: items, selectedItemId: getSelectedItemId(value, specialItems), onSelect: (item) => {
88
+ onChange(item.data);
89
+ }, onClose: closeDropdown, ariaAttributes: ariaAttributes, isCompact: true, dataTestId: "s-timezone-select-list", itemDataTestId: "s-timezone-select-item" }, searchString))] })), overlayPositionType: "sameAsTarget" }));
90
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Item describing a single timezone from the curated list.
3
+ *
4
+ * @internal
5
+ */
6
+ export interface ITimezoneItem {
7
+ /**
8
+ * IANA timezone ID, e.g. "Europe/Prague".
9
+ */
10
+ id: string;
11
+ /**
12
+ * Human readable name without the offset, e.g. "Prague" or "Pacific Time (US & Canada)".
13
+ */
14
+ name: string;
15
+ /**
16
+ * GMT offset label, e.g. "GMT+01:00".
17
+ */
18
+ offsetLabel: string;
19
+ /**
20
+ * Offset from UTC in minutes as observed in January (northern hemisphere winter).
21
+ */
22
+ januaryOffset: number;
23
+ /**
24
+ * Offset from UTC in minutes as observed in June (northern hemisphere summer).
25
+ */
26
+ juneOffset: number;
27
+ }
28
+ /**
29
+ * Returns the full display label of a timezone, combining the offset and the name,
30
+ * e.g. "GMT+01:00 Prague". Use only where the merged label is actually displayed;
31
+ * consumers that need the name or the offset alone should read the fields directly.
32
+ *
33
+ * @internal
34
+ */
35
+ export declare function getTimezoneTitle(item: Pick<ITimezoneItem, "name" | "offsetLabel">): string;
36
+ /**
37
+ * Returns the curated list of timezones.
38
+ *
39
+ * @internal
40
+ */
41
+ export declare function getTimezones(): ITimezoneItem[];
42
+ /**
43
+ * Returns the timezone from the curated list matching the provided IANA timezone ID,
44
+ * or undefined when the ID is not in the curated list.
45
+ *
46
+ * @internal
47
+ */
48
+ export declare function getTimezoneById(id: string | undefined): ITimezoneItem | undefined;
49
+ /**
50
+ * Returns the curated timezone that matches the offsets of the browser/runtime timezone.
51
+ * Falls back to UTC when no curated timezone matches.
52
+ *
53
+ * @internal
54
+ */
55
+ export declare function getUserTimezone(): ITimezoneItem;
56
+ /**
57
+ * Returns the display label used by the timezone picker: the friendly name with the GMT offset
58
+ * as a suffix in brackets, e.g. "Prague (GMT+01:00)". The name falls back to the raw IANA ID
59
+ * for zones outside the curated list, and the offset suffix is dropped when not known.
60
+ *
61
+ * The friendly name (not the IANA ID) is shown so the picker reads the same as the Home UI
62
+ * timezone settings — the id-to-name mapping is not visible to users anywhere.
63
+ *
64
+ * @internal
65
+ */
66
+ export declare function getTimezoneDisplayLabel(id: string): string;
67
+ /**
68
+ * Tests whether the timezone matches the search string. Matches on the picker display label
69
+ * ("name (offset)"), the friendly title (offset + name) and the IANA ID, with underscores
70
+ * treated as spaces so that e.g. "Los Angeles" finds "America/Los_Angeles".
71
+ *
72
+ * @internal
73
+ */
74
+ export declare function timezoneMatchesSearch(timezone: ITimezoneItem, search: string): boolean;
75
+ /**
76
+ * Returns display labels for the provided IANA timezone ID.
77
+ *
78
+ * @remarks
79
+ * The backend accepts any ICU-known timezone, so the ID may not be present in the curated list.
80
+ * In that case the raw ID is used as the name and the offset label is computed via Intl
81
+ * (empty when the runtime does not know the timezone either).
82
+ *
83
+ * @internal
84
+ */
85
+ export declare function getTimezoneLabels(id: string): Pick<ITimezoneItem, "name" | "offsetLabel">;
86
+ //# sourceMappingURL=timezones.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timezones.d.ts","sourceRoot":"","sources":["../../src/Timezone/timezones.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC1B;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IAEX;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,aAAa,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;CACtB;AAkJD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,aAAa,CAAC,GAAG,MAAM,CAE1F;AAED;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,aAAa,EAAE,CAE9C;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,CAEjF;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,IAAI,aAAa,CAkB/C;AAED;;;;;;;;;GASG;AAGH,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAWtF;AAmBD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,aAAa,CAAC,CAUzF"}