@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.
- package/esm/@ui/UiGeneralAccessRadio/UiGeneralAccessRadio.d.ts +3 -2
- package/esm/@ui/UiGeneralAccessRadio/UiGeneralAccessRadio.d.ts.map +1 -1
- package/esm/@ui/UiGeneralAccessRadio/UiGeneralAccessRadio.js +7 -3
- package/esm/@ui/UiGranteeAsyncPicker/UiGranteeAsyncPicker.d.ts +5 -0
- package/esm/@ui/UiGranteeAsyncPicker/UiGranteeAsyncPicker.d.ts.map +1 -1
- package/esm/@ui/UiGranteeAsyncPicker/UiGranteeAsyncPicker.js +3 -5
- package/esm/@ui/UiGranteeRowControls/UiGranteeRowControls.d.ts +10 -16
- package/esm/@ui/UiGranteeRowControls/UiGranteeRowControls.d.ts.map +1 -1
- package/esm/@ui/UiGranteeRowControls/UiGranteeRowControls.js +19 -32
- package/esm/@ui/UiLabelsChecklist/UiLabelsChecklist.d.ts +3 -1
- package/esm/@ui/UiLabelsChecklist/UiLabelsChecklist.d.ts.map +1 -1
- package/esm/@ui/UiLabelsChecklist/UiLabelsChecklist.js +12 -3
- package/esm/@ui/UiObjectShareDialog/UiObjectShareDialogCard.d.ts +9 -3
- package/esm/@ui/UiObjectShareDialog/UiObjectShareDialogCard.d.ts.map +1 -1
- package/esm/@ui/UiObjectShareDialog/UiObjectShareDialogCard.js +3 -3
- package/esm/@ui/UiPermissionMenu/UiPermissionMenu.d.ts +18 -10
- package/esm/@ui/UiPermissionMenu/UiPermissionMenu.d.ts.map +1 -1
- package/esm/@ui/UiPermissionMenu/UiPermissionMenu.js +45 -13
- package/esm/@ui/UiSubmenuHeader/UiSubmenuHeader.d.ts +4 -2
- package/esm/@ui/UiSubmenuHeader/UiSubmenuHeader.d.ts.map +1 -1
- package/esm/@ui/UiSubmenuHeader/UiSubmenuHeader.js +2 -2
- package/esm/Menu/positioning/PositionedMenuContent.d.ts.map +1 -1
- package/esm/Menu/positioning/PositionedMenuContent.js +10 -1
- package/esm/Overlay/Overlay.d.ts.map +1 -1
- package/esm/Overlay/Overlay.js +5 -1
- package/esm/ParameterControl/StringParameterControlDropdown.js +2 -2
- package/esm/RichText/RichTextWithTooltip.d.ts.map +1 -1
- package/esm/RichText/RichTextWithTooltip.js +3 -0
- package/esm/RichText/hooks/useEvaluatedMetricsAndAttributes.js +1 -1
- package/esm/Timezone/TimezoneSelect.d.ts +108 -0
- package/esm/Timezone/TimezoneSelect.d.ts.map +1 -0
- package/esm/Timezone/TimezoneSelect.js +90 -0
- package/esm/Timezone/timezones.d.ts +86 -0
- package/esm/Timezone/timezones.d.ts.map +1 -0
- package/esm/Timezone/timezones.js +253 -0
- package/esm/index.d.ts +3 -2
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +3 -2
- package/esm/locales.d.ts +9 -6
- package/esm/locales.d.ts.map +1 -1
- package/esm/locales.js +4 -3
- package/esm/sdk-ui-kit.d.ts +238 -61
- package/package.json +11 -11
- package/src/@ui/UiGranteeRowControls/UiGranteeRowControls.scss +0 -8
- package/src/@ui/UiLabelsChecklist/UiLabelsChecklist.scss +2 -2
- package/src/@ui/UiPermissionMenu/UiPermissionMenu.scss +5 -0
- package/src/@ui/index.scss +0 -1
- package/src/Timezone/TimezoneSelect.scss +22 -0
- package/styles/css/main.css +17 -12
- package/styles/css/main.css.map +1 -1
- package/styles/scss/main.scss +1 -0
- package/esm/@ui/UiMoreOptionsMenu/UiMoreOptionsMenu.d.ts +0 -28
- package/esm/@ui/UiMoreOptionsMenu/UiMoreOptionsMenu.d.ts.map +0 -1
- package/esm/@ui/UiMoreOptionsMenu/UiMoreOptionsMenu.js +0 -85
- 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;
|
|
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:
|
|
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,
|
|
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"}
|
package/esm/Overlay/Overlay.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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;
|
|
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"}
|