@geotab/zenith 3.17.0-beta.1 → 3.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -29
- package/dist/button/button.d.ts +1 -2
- package/dist/button/button.js +5 -22
- package/dist/button/buttonDisabledProps.d.ts +35 -0
- package/dist/button/buttonDisabledProps.js +33 -0
- package/dist/dataGrid/withSelectableRows/components/bulkActions/bulkActions.d.ts +1 -1
- package/dist/dropdownRaw/dropdownHelper.d.ts +20 -3
- package/dist/dropdownRaw/dropdownHelper.js +29 -6
- package/dist/dropdownRaw/dropdownRaw.js +22 -6
- package/dist/header/components/collapsedItemsControl/collapsedItemsControl.js +1 -1
- package/dist/header/headerButton.d.ts +15 -2
- package/dist/header/headerButton.js +11 -6
- package/dist/index.css +4 -8
- package/dist/menu/components/menuButton.js +40 -58
- package/dist/pageHeader/pageHeaderButton.d.ts +15 -1
- package/dist/pageHeader/pageHeaderButton.js +8 -6
- package/dist/pillExpandable/pillExpandable.js +3 -1
- package/dist/pillExpandable/pillExpandableSimple.d.ts +2 -1
- package/dist/pillExpandable/pillExpandableSimple.js +2 -2
- package/dist/table/selectable/interfaces.d.ts +6 -0
- package/dist/table/selectable/useDragSelection.d.ts +1 -1
- package/dist/table/selectable/useDragSelection.js +4 -4
- package/dist/table/selectable/useSelectableRows.d.ts +2 -0
- package/dist/table/selectable/useSelectableRows.js +88 -41
- package/dist/table/selectable/utils/calculateSelectionState.js +3 -2
- package/dist/table/selectable/utils/getCurrentPageSelection.js +9 -4
- package/dist/table/selectable/utils/groupExclusions.d.ts +7 -0
- package/dist/table/selectable/utils/groupExclusions.js +61 -0
- package/dist/table/selectable/utils/isRowChecked.js +1 -1
- package/dist/table/table.js +6 -5
- package/dist/tooltip/tooltip.d.ts +5 -3
- package/dist/tooltip/tooltip.js +11 -9
- package/esm/button/button.d.ts +1 -2
- package/esm/button/button.js +5 -22
- package/esm/button/buttonDisabledProps.d.ts +35 -0
- package/esm/button/buttonDisabledProps.js +29 -0
- package/esm/dataGrid/withSelectableRows/components/bulkActions/bulkActions.d.ts +1 -1
- package/esm/dropdownRaw/dropdownHelper.d.ts +20 -3
- package/esm/dropdownRaw/dropdownHelper.js +27 -5
- package/esm/dropdownRaw/dropdownRaw.js +23 -7
- package/esm/header/components/collapsedItemsControl/collapsedItemsControl.js +1 -1
- package/esm/header/headerButton.d.ts +15 -2
- package/esm/header/headerButton.js +11 -6
- package/esm/menu/components/menuButton.js +40 -58
- package/esm/pageHeader/pageHeaderButton.d.ts +15 -1
- package/esm/pageHeader/pageHeaderButton.js +8 -6
- package/esm/pillExpandable/pillExpandable.js +3 -1
- package/esm/pillExpandable/pillExpandableSimple.d.ts +2 -1
- package/esm/pillExpandable/pillExpandableSimple.js +2 -2
- package/esm/table/selectable/interfaces.d.ts +6 -0
- package/esm/table/selectable/useDragSelection.d.ts +1 -1
- package/esm/table/selectable/useDragSelection.js +4 -4
- package/esm/table/selectable/useSelectableRows.d.ts +2 -0
- package/esm/table/selectable/useSelectableRows.js +90 -43
- package/esm/table/selectable/utils/calculateSelectionState.js +3 -2
- package/esm/table/selectable/utils/getCurrentPageSelection.js +9 -4
- package/esm/table/selectable/utils/groupExclusions.d.ts +7 -0
- package/esm/table/selectable/utils/groupExclusions.js +55 -0
- package/esm/table/selectable/utils/isRowChecked.js +1 -1
- package/esm/table/table.js +6 -5
- package/esm/tooltip/tooltip.d.ts +5 -3
- package/esm/tooltip/tooltip.js +11 -9
- package/package.json +1 -1
- package/README.md.baseline +0 -1
package/dist/table/table.js
CHANGED
|
@@ -148,6 +148,7 @@ const useTableChildren_1 = require("./children/useTableChildren");
|
|
|
148
148
|
const deviceType_1 = require("../commonHelpers/hooks/deviceType");
|
|
149
149
|
const useMobile_1 = require("../commonHelpers/hooks/useMobile");
|
|
150
150
|
const getCurrentPageSelection_1 = require("./selectable/utils/getCurrentPageSelection");
|
|
151
|
+
const groupExclusions_1 = require("./selectable/utils/groupExclusions");
|
|
151
152
|
const tableFullscreen_1 = require("./children/tableFullscreen");
|
|
152
153
|
const tableBulkActions_1 = require("./children/tableBulkActions");
|
|
153
154
|
const tablePagination_1 = require("./children/tablePagination");
|
|
@@ -235,7 +236,7 @@ const TableInner = ({
|
|
|
235
236
|
active,
|
|
236
237
|
onClick
|
|
237
238
|
}) => {
|
|
238
|
-
var _a, _b, _c, _d, _e;
|
|
239
|
+
var _a, _b, _c, _d, _e, _f;
|
|
239
240
|
const isMobile = (0, useMobile_1.useMobile)();
|
|
240
241
|
const {
|
|
241
242
|
translate
|
|
@@ -329,15 +330,15 @@ const TableInner = ({
|
|
|
329
330
|
var _a, _b;
|
|
330
331
|
return (0, useSelectableRows_1.isAllSelection)((_a = selectable === null || selectable === void 0 ? void 0 : selectable.selection) === null || _a === void 0 ? void 0 : _a.all, (_b = selectable === null || selectable === void 0 ? void 0 : selectable.selection) === null || _b === void 0 ? void 0 : _b.selected.length, selectable === null || selectable === void 0 ? void 0 : selectable.allowExclusions);
|
|
331
332
|
}, [(_c = selectable === null || selectable === void 0 ? void 0 : selectable.selection) === null || _c === void 0 ? void 0 : _c.all, selectable === null || selectable === void 0 ? void 0 : selectable.allowExclusions, (_d = selectable === null || selectable === void 0 ? void 0 : selectable.selection) === null || _d === void 0 ? void 0 : _d.selected.length]);
|
|
333
|
+
const resolvedSelection = (0, react_1.useMemo)(() => (0, groupExclusions_1.resolveGroupExclusions)(entities, selectable), [entities, selectable]);
|
|
332
334
|
const selectionQty = (0, react_1.useMemo)(() => {
|
|
333
|
-
var _a;
|
|
334
335
|
if (!selectable) {
|
|
335
336
|
return 0;
|
|
336
337
|
}
|
|
337
|
-
const ids = new Set(
|
|
338
|
+
const ids = new Set(resolvedSelection === null || resolvedSelection === void 0 ? void 0 : resolvedSelection.selected);
|
|
338
339
|
const base = entities.length > 0 ? entities : loadingEntities;
|
|
339
340
|
return allSelected ? [...(0, getCurrentPageSelection_1.getCurrentPageSelection)(base, selectable)].filter(id => !selectable.allowExclusions || !ids.has(id)).length : ids.size;
|
|
340
|
-
}, [selectable, entities, loadingEntities, allSelected]);
|
|
341
|
+
}, [selectable, resolvedSelection, entities, loadingEntities, allSelected]);
|
|
341
342
|
const clearSelection = (0, react_1.useCallback)(() => {
|
|
342
343
|
setSelectMode(false);
|
|
343
344
|
selectable === null || selectable === void 0 ? void 0 : selectable.onSelect({
|
|
@@ -363,7 +364,7 @@ const TableInner = ({
|
|
|
363
364
|
detailPanel,
|
|
364
365
|
footer,
|
|
365
366
|
other
|
|
366
|
-
} = (0, useTableChildren_1.useTableChildren)(children, isMobile, selectionQty, allSelected, clearSelection, activeId, gridRef, onCloseDetailPanel, isInfinite, entities.length, (selectable === null || selectable === void 0 ? void 0 : selectable.allowExclusions) && ((_e =
|
|
367
|
+
} = (0, useTableChildren_1.useTableChildren)(children, isMobile, selectionQty, allSelected, clearSelection, activeId, gridRef, onCloseDetailPanel, isInfinite, entities.length, (selectable === null || selectable === void 0 ? void 0 : selectable.allowExclusions) && (resolvedSelection === null || resolvedSelection === void 0 ? void 0 : resolvedSelection.all) ? new Set(resolvedSelection.selected).size + ((_f = (_e = resolvedSelection.excludedGroupIds) === null || _e === void 0 ? void 0 : _e.length) !== null && _f !== void 0 ? _f : 0) : 0, (selectable === null || selectable === void 0 ? void 0 : selectable.strategy) !== "nested" && !(selectable === null || selectable === void 0 ? void 0 : selectable.checkboxDisabled) && !(selectable === null || selectable === void 0 ? void 0 : selectable.checkboxVisible), !!feedEndCallback || !!isLoading);
|
|
367
368
|
const hasDetailPanel = !!detailPanel;
|
|
368
369
|
const shouldEnableRowClick = hasDetailPanel || !!onClick;
|
|
369
370
|
const activeIdForRow = shouldEnableRowClick ? activeId : undefined;
|
|
@@ -5,11 +5,13 @@ import { TAlignment } from "../absolute/absolute";
|
|
|
5
5
|
import { IInlineTrigger } from "./components/inlineTrigger";
|
|
6
6
|
export type TooltipSize = "large" | "small" | "auto";
|
|
7
7
|
export type TMobileView = "default" | "mobileSheet";
|
|
8
|
+
interface ITriggerDisabledProps {
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
"aria-disabled"?: boolean | "true" | "false";
|
|
11
|
+
}
|
|
8
12
|
interface ITooltip extends IZenComponentProps {
|
|
9
|
-
trigger?: ReactElement<PropsWithChildren & {
|
|
13
|
+
trigger?: ReactElement<PropsWithChildren & ITriggerDisabledProps & {
|
|
10
14
|
onClick?: (e: React.MouseEvent) => void;
|
|
11
|
-
disabled?: boolean;
|
|
12
|
-
focusableWhenDisabled?: boolean;
|
|
13
15
|
}>;
|
|
14
16
|
alignment?: TAlignment;
|
|
15
17
|
triggerClassName?: string;
|
package/dist/tooltip/tooltip.js
CHANGED
|
@@ -159,12 +159,14 @@ const getInitialDeviceType = () => {
|
|
|
159
159
|
}
|
|
160
160
|
return deviceType_1.DeviceType.Desktop;
|
|
161
161
|
};
|
|
162
|
+
// A disabled trigger (native `disabled`, or `aria-disabled` when it must stay focusable) never runs its own
|
|
163
|
+
// `onClick`, so the tooltip has to react to the click capture phase instead.
|
|
164
|
+
const isTriggerDisabled = props => Boolean(props === null || props === void 0 ? void 0 : props.disabled) || (props === null || props === void 0 ? void 0 : props["aria-disabled"]) === true || (props === null || props === void 0 ? void 0 : props["aria-disabled"]) === "true";
|
|
162
165
|
const EventWrapper = ({
|
|
163
166
|
children,
|
|
164
167
|
deviceType,
|
|
165
168
|
onKeyDownHandler,
|
|
166
169
|
handleUniversalClick,
|
|
167
|
-
isTriggerDisabledFocusable,
|
|
168
170
|
setTooltipVisible,
|
|
169
171
|
timerRef,
|
|
170
172
|
timerMouseLeaveRef,
|
|
@@ -173,8 +175,9 @@ const EventWrapper = ({
|
|
|
173
175
|
const isCurrentDeviceDesktop = deviceType === deviceType_1.DeviceType.Desktop;
|
|
174
176
|
const triggerProps = children.props;
|
|
175
177
|
const originalOnClick = triggerProps.onClick;
|
|
176
|
-
// A disabled
|
|
177
|
-
//
|
|
178
|
+
// A disabled trigger doesn't run its own `onClick`, so on mobile the tap is handled in the capture phase
|
|
179
|
+
// instead — see `mobileEvents` below.
|
|
180
|
+
const isDisabledTrigger = isTriggerDisabled(triggerProps);
|
|
178
181
|
const composedOnClick = (0, react_1.useCallback)(e => {
|
|
179
182
|
// Call original onClick first if it exists
|
|
180
183
|
if (originalOnClick) {
|
|
@@ -213,7 +216,7 @@ const EventWrapper = ({
|
|
|
213
216
|
}, [handleUniversalClick]);
|
|
214
217
|
const mobileEvents = Object.assign({
|
|
215
218
|
onKeyDown: mobileOnKeyDown
|
|
216
|
-
},
|
|
219
|
+
}, isDisabledTrigger ? {
|
|
217
220
|
onClickCapture: handleUniversalClick
|
|
218
221
|
} : {
|
|
219
222
|
onClick: composedOnClick
|
|
@@ -248,7 +251,7 @@ const Tooltip = ({
|
|
|
248
251
|
const [deviceType, setDeviceType] = (0, react_1.useState)(getInitialDeviceType);
|
|
249
252
|
const isMobile = deviceType === deviceType_1.DeviceType.Mobile;
|
|
250
253
|
const triggerProps = trigger === null || trigger === void 0 ? void 0 : trigger.props;
|
|
251
|
-
const
|
|
254
|
+
const isDisabledTrigger = isTriggerDisabled(triggerProps);
|
|
252
255
|
const tooltipId = (0, react_1.useId)();
|
|
253
256
|
const {
|
|
254
257
|
translate
|
|
@@ -279,7 +282,7 @@ const Tooltip = ({
|
|
|
279
282
|
}
|
|
280
283
|
};
|
|
281
284
|
const handleUniversalClick = (0, react_1.useCallback)(e => {
|
|
282
|
-
const hasExternalClickHandler = !!(triggerProps === null || triggerProps === void 0 ? void 0 : triggerProps.onClick) && !
|
|
285
|
+
const hasExternalClickHandler = !!(triggerProps === null || triggerProps === void 0 ? void 0 : triggerProps.onClick) && !isDisabledTrigger;
|
|
283
286
|
if (deviceType === deviceType_1.DeviceType.Desktop) {
|
|
284
287
|
setTooltipVisible(currentVisible => {
|
|
285
288
|
if (currentVisible) {
|
|
@@ -288,14 +291,14 @@ const Tooltip = ({
|
|
|
288
291
|
return currentVisible;
|
|
289
292
|
});
|
|
290
293
|
} else {
|
|
291
|
-
if (!
|
|
294
|
+
if (!isDisabledTrigger) {
|
|
292
295
|
e === null || e === void 0 ? void 0 : e.stopPropagation();
|
|
293
296
|
}
|
|
294
297
|
if (!hasExternalClickHandler) {
|
|
295
298
|
setTooltipVisible(isVisible => !isVisible);
|
|
296
299
|
}
|
|
297
300
|
}
|
|
298
|
-
}, [deviceType,
|
|
301
|
+
}, [deviceType, isDisabledTrigger, triggerProps]);
|
|
299
302
|
// Apply `triggerAriaLabel` to a `Tooltip.InlineTrigger` trigger (it has no `ariaLabel` prop
|
|
300
303
|
// of its own); the default info-icon trigger below handles `triggerAriaLabel` separately.
|
|
301
304
|
// An `aria-label` set directly on the `Tooltip.InlineTrigger` element takes priority over
|
|
@@ -322,7 +325,6 @@ const Tooltip = ({
|
|
|
322
325
|
isTooltipVisible: isTooltipVisible,
|
|
323
326
|
onKeyDownHandler: onKeyDownHandler,
|
|
324
327
|
handleUniversalClick: handleUniversalClick,
|
|
325
|
-
isTriggerDisabledFocusable: isTriggerDisabledFocusable,
|
|
326
328
|
setTooltipVisible: setTooltipVisible,
|
|
327
329
|
timerRef: timerRef,
|
|
328
330
|
timerMouseLeaveRef: timerMouseLeaveRef,
|
package/esm/button/button.d.ts
CHANGED
|
@@ -9,7 +9,6 @@ export interface IButton extends IZenComponentProps, IZenGridItem {
|
|
|
9
9
|
title?: string;
|
|
10
10
|
type?: TButton;
|
|
11
11
|
disabled?: boolean;
|
|
12
|
-
focusableWhenDisabled?: boolean;
|
|
13
12
|
onClick?: React.MouseEventHandler;
|
|
14
13
|
onBlur?: React.FocusEventHandler;
|
|
15
14
|
onMouseOver?: (e: React.MouseEvent<HTMLElement>) => void;
|
|
@@ -28,6 +27,6 @@ export interface IButton extends IZenComponentProps, IZenGridItem {
|
|
|
28
27
|
dataAnalyticsId?: string;
|
|
29
28
|
}
|
|
30
29
|
export declare const Button: {
|
|
31
|
-
({ className, type, disabled,
|
|
30
|
+
({ className, type, disabled, id, title, onClick, onMouseOver, onMouseOut, onMouseDown, onMouseUp, onKeyDown, onKeyUp, children, link, target, htmlType, rel, ref, name, onBlur, role, ariaLabel, dataAnalyticsId, ...other }: IButton): import("react/jsx-runtime").JSX.Element;
|
|
32
31
|
displayName: string;
|
|
33
32
|
};
|
package/esm/button/button.js
CHANGED
|
@@ -15,35 +15,18 @@ import { classNames } from "../commonHelpers/classNames/classNames";
|
|
|
15
15
|
import { useDriveClassName } from "../utils/theme/useDriveClassName";
|
|
16
16
|
import { filterGridProps } from "../gridLayout/utils/filterGridProps";
|
|
17
17
|
import { useMobileClassName } from "../utils/theme/useMobileClassName";
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
event.stopPropagation();
|
|
21
|
-
};
|
|
22
|
-
// Guards on `disabled` alone, not just `focusableWhenDisabled`, so clicks are blocked in JS regardless of
|
|
23
|
-
// CSS. `onKeyDown`/`onKeyUp` stay unguarded — Tooltip needs them.
|
|
24
|
-
const getDisabledGuardedHandlers = (disabled, isFocusableWhenDisabled, onClick, onMouseDown, onMouseUp) => ({
|
|
25
|
-
onClick: disabled ? blockActivation : onClick,
|
|
26
|
-
onMouseDown: isFocusableWhenDisabled ? blockActivation : onMouseDown,
|
|
27
|
-
onMouseUp: isFocusableWhenDisabled ? blockActivation : onMouseUp
|
|
28
|
-
});
|
|
29
|
-
const getButtonClassName = (type, disabled, isFocusableWhenDisabled, mobileOrDriveClasses, className) => {
|
|
18
|
+
export const Button = (_a) => {
|
|
19
|
+
var { className, type, disabled, id, title, onClick, onMouseOver, onMouseOut, onMouseDown, onMouseUp, onKeyDown, onKeyUp, children, link, target, htmlType, rel, ref, name, onBlur, role, ariaLabel, dataAnalyticsId } = _a, other = __rest(_a, ["className", "type", "disabled", "id", "title", "onClick", "onMouseOver", "onMouseOut", "onMouseDown", "onMouseUp", "onKeyDown", "onKeyUp", "children", "link", "target", "htmlType", "rel", "ref", "name", "onBlur", "role", "ariaLabel", "dataAnalyticsId"]);
|
|
30
20
|
const modifierClassName = type && type !== ButtonType.Secondary ? `zen-button--${type}` : "";
|
|
31
21
|
const disabledClassName = disabled ? "zen-button--disabled" : "";
|
|
32
|
-
const disabledFocusableClassName = isFocusableWhenDisabled ? "zen-button--disabled-focusable" : "";
|
|
33
|
-
return classNames(["zen-button", mobileOrDriveClasses, modifierClassName, disabledClassName, disabledFocusableClassName, className !== null && className !== void 0 ? className : ""]);
|
|
34
|
-
};
|
|
35
|
-
export const Button = (_a) => {
|
|
36
|
-
var { className, type, disabled, focusableWhenDisabled, id, title, onClick, onMouseOver, onMouseOut, onMouseDown, onMouseUp, onKeyDown, onKeyUp, children, link, target, htmlType, rel, ref, name, onBlur, role, ariaLabel, dataAnalyticsId } = _a, other = __rest(_a, ["className", "type", "disabled", "focusableWhenDisabled", "id", "title", "onClick", "onMouseOver", "onMouseOut", "onMouseDown", "onMouseUp", "onKeyDown", "onKeyUp", "children", "link", "target", "htmlType", "rel", "ref", "name", "onBlur", "role", "ariaLabel", "dataAnalyticsId"]);
|
|
37
|
-
const isFocusableWhenDisabled = Boolean(disabled) && Boolean(focusableWhenDisabled);
|
|
38
22
|
const driveClasses = useDriveClassName("zen-button");
|
|
39
23
|
const ariaLabelValue = ariaLabel || title;
|
|
40
24
|
const mobileClasses = useMobileClassName("zen-button");
|
|
41
25
|
const commonArgs = Object.assign(Object.assign(Object.assign({ id,
|
|
42
|
-
title }, (ariaLabelValue ? { "aria-label": ariaLabelValue } : {})), (dataAnalyticsId ? { "data-analytics-id": dataAnalyticsId } : {})), { className:
|
|
43
|
-
const { onClick: onClickWithDisabledGuard, onMouseDown: onMouseDownWithDisabledGuard, onMouseUp: onMouseUpWithDisabledGuard } = getDisabledGuardedHandlers(disabled, isFocusableWhenDisabled, onClick, onMouseDown, onMouseUp);
|
|
26
|
+
title }, (ariaLabelValue ? { "aria-label": ariaLabelValue } : {})), (dataAnalyticsId ? { "data-analytics-id": dataAnalyticsId } : {})), { className: classNames(["zen-button", mobileClasses || driveClasses || "", modifierClassName, disabledClassName, className !== null && className !== void 0 ? className : ""]) });
|
|
44
27
|
if (link) {
|
|
45
|
-
return (_jsx("a", Object.assign({ role: "link", rel: rel, target: target, href: disabled ? undefined : link,
|
|
28
|
+
return (_jsx("a", Object.assign({ role: "link", rel: rel, target: target, href: disabled ? undefined : link, "aria-disabled": disabled, onKeyDown: onKeyDown, onKeyUp: onKeyUp, onMouseOver: onMouseOver, onMouseOut: onMouseOut, onClick: onClick }, commonArgs, filterGridProps(other), { children: children })));
|
|
46
29
|
}
|
|
47
|
-
return (_jsx("button", Object.assign({ name: name, onBlur: onBlur, onClick:
|
|
30
|
+
return (_jsx("button", Object.assign({ name: name, onBlur: onBlur, onClick: onClick, onMouseOver: onMouseOver, onMouseOut: onMouseOut, onMouseDown: onMouseDown, onMouseUp: onMouseUp, onKeyDown: onKeyDown, onKeyUp: onKeyUp, ref: ref, type: htmlType, disabled: disabled, role: role }, commonArgs, filterGridProps(other), { children: children })));
|
|
48
31
|
};
|
|
49
32
|
Button.displayName = "Button";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export declare const BUTTON_DISABLED_CLASS = "zen-button--disabled";
|
|
3
|
+
export declare const BUTTON_DISABLED_FOCUSABLE_CLASS = "zen-button--disabled-focusable";
|
|
4
|
+
export interface IButtonDisabledOptions {
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Keeps a disabled button in the tab order and hoverable, using `aria-disabled` instead of the native
|
|
8
|
+
* `disabled` attribute. Has no effect while the button is enabled.
|
|
9
|
+
*/
|
|
10
|
+
focusable?: boolean;
|
|
11
|
+
/** The `link` prop of the button, a link without `href` is not tabbable, so it needs an explicit `tabIndex`. */
|
|
12
|
+
link?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface IButtonDisabledProps {
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
"aria-disabled"?: boolean;
|
|
17
|
+
tabIndex?: number;
|
|
18
|
+
className?: string;
|
|
19
|
+
onClick?: (event: React.SyntheticEvent) => void;
|
|
20
|
+
onMouseDown?: (event: React.SyntheticEvent) => void;
|
|
21
|
+
onMouseUp?: (event: React.SyntheticEvent) => void;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Props that make `Button` (or `TextIconButton`) disabled. `Button` itself only applies them.
|
|
25
|
+
*
|
|
26
|
+
* - enabled: `{ disabled }`
|
|
27
|
+
* - disabled: native `disabled`, the button leaves the tab order
|
|
28
|
+
* - disabled and `focusable`: `aria-disabled`, activation is blocked, the button stays focusable and hoverable
|
|
29
|
+
*
|
|
30
|
+
* `onKeyDown`/`onKeyUp` are left untouched: Tooltip needs them. Enter/Space on a focused button without the native
|
|
31
|
+
* `disabled` attribute fire a click, which is blocked here.
|
|
32
|
+
*
|
|
33
|
+
* The caller merges the returned `className` with its own and wraps the button in whatever explains the state.
|
|
34
|
+
*/
|
|
35
|
+
export declare const getButtonDisabledProps: ({ disabled, focusable, link }: IButtonDisabledOptions) => IButtonDisabledProps;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { classNames } from "../commonHelpers/classNames/classNames";
|
|
2
|
+
// `button.less` styles these classes: `--disabled-focusable` keeps pointer events so hover/tooltip still work.
|
|
3
|
+
export const BUTTON_DISABLED_CLASS = "zen-button--disabled";
|
|
4
|
+
export const BUTTON_DISABLED_FOCUSABLE_CLASS = "zen-button--disabled-focusable";
|
|
5
|
+
const blockActivation = (event) => {
|
|
6
|
+
event.preventDefault();
|
|
7
|
+
event.stopPropagation();
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Props that make `Button` (or `TextIconButton`) disabled. `Button` itself only applies them.
|
|
11
|
+
*
|
|
12
|
+
* - enabled: `{ disabled }`
|
|
13
|
+
* - disabled: native `disabled`, the button leaves the tab order
|
|
14
|
+
* - disabled and `focusable`: `aria-disabled`, activation is blocked, the button stays focusable and hoverable
|
|
15
|
+
*
|
|
16
|
+
* `onKeyDown`/`onKeyUp` are left untouched: Tooltip needs them. Enter/Space on a focused button without the native
|
|
17
|
+
* `disabled` attribute fire a click, which is blocked here.
|
|
18
|
+
*
|
|
19
|
+
* The caller merges the returned `className` with its own and wraps the button in whatever explains the state.
|
|
20
|
+
*/
|
|
21
|
+
export const getButtonDisabledProps = ({ disabled, focusable, link }) => {
|
|
22
|
+
if (!disabled || !focusable) {
|
|
23
|
+
return { disabled };
|
|
24
|
+
}
|
|
25
|
+
const hasLink = Boolean(link);
|
|
26
|
+
return Object.assign(Object.assign({}, (hasLink ? { disabled: true, tabIndex: 0 } : { "aria-disabled": true })), {
|
|
27
|
+
// Button adds `zen-button--disabled` to a disabled link itself.
|
|
28
|
+
className: classNames([hasLink ? "" : BUTTON_DISABLED_CLASS, BUTTON_DISABLED_FOCUSABLE_CLASS]), onClick: blockActivation, onMouseDown: blockActivation, onMouseUp: blockActivation });
|
|
29
|
+
};
|
|
@@ -12,7 +12,7 @@ export interface IBulkActionsProps extends IZenComponentProps {
|
|
|
12
12
|
* even when `selected` is 0 (i.e., cross-page "select all" is active).
|
|
13
13
|
*/
|
|
14
14
|
allSelected?: boolean;
|
|
15
|
-
/**
|
|
15
|
+
/** The selection spans unloaded rows and its total is not available. */
|
|
16
16
|
selectionCountUnknown?: boolean;
|
|
17
17
|
onClearSelection: () => void;
|
|
18
18
|
onSelectAll?: () => void;
|
|
@@ -12,12 +12,28 @@ export declare const isAllItemsHasOneParent: (listData: IDropdownRawItem[], chil
|
|
|
12
12
|
export declare function prepareComboOptionsForList(actionListData: Set<string>, translate: (s: string) => string, options: IDropdownRawItem[], getSelectedFn: () => string[], getSelectedChildFn: (optionId: any) => string[], isFlatList: boolean, getTitle?: (itemId: string) => string, isAllSelected?: boolean): ICheckboxListWithActionOption[];
|
|
13
13
|
export declare const createOptions: (stateObj: IDropdownState, translate: (s: string) => string, listItem: IDropdownRawItem[], isAllSelected?: boolean) => ICheckboxListWithActionOption[];
|
|
14
14
|
export declare const sortDropdownItemArray: (arr: IDropdownRawItem[], sortFn?: (a: IDropdownRawItem, b: IDropdownRawItem) => number) => IDropdownRawItem[];
|
|
15
|
+
/**
|
|
16
|
+
* An item is half selected when at least one of its direct children is selected — the state its checkbox
|
|
17
|
+
* shows as indeterminate (see `prepareComboOptionsForList`). `selectedSet` is passed in so the sort can use
|
|
18
|
+
* its own snapshot of the selection instead of the live one.
|
|
19
|
+
*/
|
|
20
|
+
export declare const hasSelectedChild: (groupsMap: IDropdownState["groupsMap"], itemId: string, selectedSet: Set<string>) => boolean;
|
|
21
|
+
interface ISortSelectedToTopOptions {
|
|
22
|
+
/** Items that count as selected although their own id is not, e.g. a half selected parent. */
|
|
23
|
+
isPartiallySelected?: (item: IDropdownRawItem) => boolean;
|
|
24
|
+
/** Disabled items cannot be toggled, so they follow the selectable ones. */
|
|
25
|
+
isDisabled?: (item: IDropdownRawItem) => boolean;
|
|
26
|
+
}
|
|
15
27
|
/**
|
|
16
28
|
* Floats selected items to the top of the list while preserving the relative order within each
|
|
17
|
-
* group
|
|
18
|
-
*
|
|
29
|
+
* group. The order is: selected items that can be toggled, then selected items that are disabled
|
|
30
|
+
* (so a newly selected item is never pushed down by disabled ones), then everything else. An item
|
|
31
|
+
* counts as selected when its own id is in `selectedIds` or when `isPartiallySelected` returns true
|
|
32
|
+
* for it, so half selected items sit with the selected ones. When `selectedIds` is empty the original
|
|
33
|
+
* array is returned unchanged (no allocation). Uses a single-pass partition to avoid traversing
|
|
34
|
+
* `items` more than once.
|
|
19
35
|
*/
|
|
20
|
-
export declare const applySortSelectedToTop: (items: IDropdownRawItem[], selectedIds: string[]) => IDropdownRawItem[];
|
|
36
|
+
export declare const applySortSelectedToTop: (items: IDropdownRawItem[], selectedIds: string[], { isPartiallySelected, isDisabled }?: ISortSelectedToTopOptions) => IDropdownRawItem[];
|
|
21
37
|
export declare const createSingleListOptions: (stateObj: IDropdownState, translate: (s: string) => string, listItem: IDropdownRawItem[]) => {
|
|
22
38
|
destructive: boolean | undefined;
|
|
23
39
|
divider?: boolean | undefined;
|
|
@@ -56,3 +72,4 @@ export declare const getAllyTriggerProps: (allyPattern: "selectOnlyCombobox" | "
|
|
|
56
72
|
readonly role: "button";
|
|
57
73
|
readonly "aria-haspopup": "menu";
|
|
58
74
|
};
|
|
75
|
+
export {};
|
|
@@ -81,21 +81,43 @@ export const sortDropdownItemArray = (arr, sortFn) => {
|
|
|
81
81
|
}
|
|
82
82
|
return arr;
|
|
83
83
|
};
|
|
84
|
+
/**
|
|
85
|
+
* An item is half selected when at least one of its direct children is selected — the state its checkbox
|
|
86
|
+
* shows as indeterminate (see `prepareComboOptionsForList`). `selectedSet` is passed in so the sort can use
|
|
87
|
+
* its own snapshot of the selection instead of the live one.
|
|
88
|
+
*/
|
|
89
|
+
export const hasSelectedChild = (groupsMap, itemId, selectedSet) => {
|
|
90
|
+
const group = groupsMap[itemId];
|
|
91
|
+
return group ? getChildList(group.children).some(child => selectedSet.has(child.id)) : false;
|
|
92
|
+
};
|
|
84
93
|
/**
|
|
85
94
|
* Floats selected items to the top of the list while preserving the relative order within each
|
|
86
|
-
* group
|
|
87
|
-
*
|
|
95
|
+
* group. The order is: selected items that can be toggled, then selected items that are disabled
|
|
96
|
+
* (so a newly selected item is never pushed down by disabled ones), then everything else. An item
|
|
97
|
+
* counts as selected when its own id is in `selectedIds` or when `isPartiallySelected` returns true
|
|
98
|
+
* for it, so half selected items sit with the selected ones. When `selectedIds` is empty the original
|
|
99
|
+
* array is returned unchanged (no allocation). Uses a single-pass partition to avoid traversing
|
|
100
|
+
* `items` more than once.
|
|
88
101
|
*/
|
|
89
|
-
export const applySortSelectedToTop = (items, selectedIds) => {
|
|
102
|
+
export const applySortSelectedToTop = (items, selectedIds, { isPartiallySelected, isDisabled } = {}) => {
|
|
90
103
|
if (!selectedIds.length)
|
|
91
104
|
return items;
|
|
92
105
|
const selectedSet = new Set(selectedIds);
|
|
93
106
|
const selected = [];
|
|
107
|
+
const selectedDisabled = [];
|
|
94
108
|
const unselected = [];
|
|
95
109
|
for (const item of items) {
|
|
96
|
-
(selectedSet.has(item.id) ?
|
|
110
|
+
if (!selectedSet.has(item.id) && !(isPartiallySelected === null || isPartiallySelected === void 0 ? void 0 : isPartiallySelected(item))) {
|
|
111
|
+
unselected.push(item);
|
|
112
|
+
}
|
|
113
|
+
else if (isDisabled === null || isDisabled === void 0 ? void 0 : isDisabled(item)) {
|
|
114
|
+
selectedDisabled.push(item);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
selected.push(item);
|
|
118
|
+
}
|
|
97
119
|
}
|
|
98
|
-
return [...selected, ...unselected];
|
|
120
|
+
return [...selected, ...selectedDisabled, ...unselected];
|
|
99
121
|
};
|
|
100
122
|
export const createSingleListOptions = (stateObj, translate, listItem) => {
|
|
101
123
|
const preparedOptions = listItem && listItem.length
|
|
@@ -8,7 +8,7 @@ import { getInitialState, stateReducer, ROOT_GROUP_ID } from "./stateReducer/sta
|
|
|
8
8
|
import { StateActionType } from "./stateReducer/stateActionType";
|
|
9
9
|
import { FOCUSABLE_SELECTOR } from "../utils/focusableSelector";
|
|
10
10
|
import { useEscape } from "../commonHelpers/hooks/useEscape";
|
|
11
|
-
import { applySortSelectedToTop, checkIsDataProblem, compareObjectsArrays, compareStringsArrays, createListDataOptions, getDataForChange, getListDataWithDisabled, getSelectedCount, getStringFromAllSelected, getStringFromSelected, isAllChildrenSelected, isAllItemsHasOneParent, isEveryItemSelected, prepareSelectedIdsToItems, sortDropdownItemArray } from "./dropdownHelper";
|
|
11
|
+
import { applySortSelectedToTop, hasSelectedChild, checkIsDataProblem, compareObjectsArrays, compareStringsArrays, createListDataOptions, getDataForChange, getListDataWithDisabled, getSelectedCount, getStringFromAllSelected, getStringFromSelected, isAllChildrenSelected, isAllItemsHasOneParent, isEveryItemSelected, prepareSelectedIdsToItems, sortDropdownItemArray } from "./dropdownHelper";
|
|
12
12
|
import { DropdownTrigger } from "./dropdownTrigger";
|
|
13
13
|
import { isFocusable } from "../dialog/dialogHelpers";
|
|
14
14
|
import { useLanguage } from "../utils/localization/useLanguage";
|
|
@@ -1147,20 +1147,36 @@ export const DropdownRaw = props => {
|
|
|
1147
1147
|
// hasApplyButton=true: uses globalSelectedIds (committed selection) — reordering
|
|
1148
1148
|
// only takes effect after the user clicks "Apply".
|
|
1149
1149
|
const idsForSort = hasApplyButton ? state.globalSelectedIds : state.sortedTopSnapshot;
|
|
1150
|
-
const floated = sortSelectedToTop ? applySortSelectedToTop(children, idsForSort) : children;
|
|
1151
|
-
rawItems = floated.slice(0, listLimit);
|
|
1152
1150
|
if (sortSelectedToTop) {
|
|
1151
|
+
// Half selected items (a direct child is selected, shown as indeterminate) float with the selected ones.
|
|
1152
|
+
// Disabled selected items follow the selectable ones; in a nested list `disabled` is not enforced (see createOptions).
|
|
1153
1153
|
const selectedSet = new Set(idsForSort);
|
|
1154
|
-
|
|
1154
|
+
const isPartiallySelected = item => hasSelectedChild(state.groupsMap, item.id, selectedSet);
|
|
1155
|
+
const isDisabled = item => Boolean(item.disabled) && !state.isNestedList;
|
|
1156
|
+
rawItems = applySortSelectedToTop(children, idsForSort, {
|
|
1157
|
+
isPartiallySelected,
|
|
1158
|
+
isDisabled
|
|
1159
|
+
}).slice(0, listLimit);
|
|
1160
|
+
pinnedCount = rawItems.filter(item => selectedSet.has(item.id) || isPartiallySelected(item)).length;
|
|
1161
|
+
} else {
|
|
1162
|
+
rawItems = children.slice(0, listLimit);
|
|
1155
1163
|
}
|
|
1156
1164
|
}
|
|
1157
1165
|
const listData = createListDataOptions(state, translate, rawItems, multiselect, currentAllSelected);
|
|
1158
1166
|
// Explicit bounds check (rather than relying on CheckboxListWithAction's own
|
|
1159
1167
|
// `ind < optionsArr.length - 1` guard) keeps the "don't draw a trailing divider"
|
|
1160
1168
|
// intent local to where the boundary is computed.
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1169
|
+
// Floated items drop their own dividers, so the boundary divider is the only one drawn between them and the rest.
|
|
1170
|
+
const currentListData = multiselect && sortSelectedToTop && pinnedCount > 0 && pinnedCount < listData.length ? listData.map((opt, ind) => {
|
|
1171
|
+
if (ind === pinnedCount - 1) {
|
|
1172
|
+
return Object.assign(Object.assign({}, opt), {
|
|
1173
|
+
divider: true
|
|
1174
|
+
});
|
|
1175
|
+
}
|
|
1176
|
+
return ind < pinnedCount && opt.divider ? Object.assign(Object.assign({}, opt), {
|
|
1177
|
+
divider: false
|
|
1178
|
+
}) : opt;
|
|
1179
|
+
}) : listData;
|
|
1164
1180
|
return _jsx(DropdownList, {
|
|
1165
1181
|
onBackButtonClick: handleBackButtonClick,
|
|
1166
1182
|
onSelectAllClick: isSearchMode ? handleSelectAllInSearch : handleSelectAllClick,
|
|
@@ -102,7 +102,7 @@ export const PageToolbarCollapsedItemsControl = ({
|
|
|
102
102
|
rel: child.props.rel,
|
|
103
103
|
icon: child.props.icon,
|
|
104
104
|
iconOpen: child.props.iconOpen,
|
|
105
|
-
destructive: child.props.
|
|
105
|
+
destructive: child.props.type === ButtonType.Destructive || child.props.type === ButtonType.TertiaryDestructive
|
|
106
106
|
});
|
|
107
107
|
items.push(el);
|
|
108
108
|
}
|
|
@@ -2,20 +2,33 @@ import { IButton } from "../button/button";
|
|
|
2
2
|
import { TButtonIconPosition } from "../textIconButton/textIconButton";
|
|
3
3
|
import { TSvgIconSize } from "../svgIcon/svgIconSize";
|
|
4
4
|
import { IIcon } from "../icons/icon";
|
|
5
|
+
import { TAlignment } from "../absolute/absolute";
|
|
5
6
|
export interface IHeaderButton extends Omit<IButton, "onClick"> {
|
|
6
7
|
id: string;
|
|
7
8
|
important?: boolean;
|
|
8
9
|
inContextMenu?: boolean;
|
|
9
10
|
icon?: React.FC<IIcon>;
|
|
10
11
|
iconOpen?: boolean;
|
|
11
|
-
destructive?: boolean;
|
|
12
12
|
iconPosition?: TButtonIconPosition;
|
|
13
13
|
iconClasses?: string;
|
|
14
14
|
iconSize?: TSvgIconSize;
|
|
15
|
+
/**
|
|
16
|
+
* Tooltip content shown only while the button is `disabled`.
|
|
17
|
+
*
|
|
18
|
+
* When `disabled` and `disabledTooltip` are both set, the button uses `aria-disabled` instead of the native
|
|
19
|
+
* `disabled` attribute, so it stays focusable and hoverable and the tooltip can explain why it is unavailable.
|
|
20
|
+
* In every other case the standard `disabled` attribute is used, and `disabledTooltip` is ignored while the
|
|
21
|
+
* button is enabled.
|
|
22
|
+
*/
|
|
23
|
+
disabledTooltip?: React.ReactNode;
|
|
15
24
|
onClick?: (id: string, event: React.MouseEvent) => void;
|
|
16
25
|
}
|
|
26
|
+
interface IHeaderButtonInternal {
|
|
27
|
+
tooltipAlignment?: TAlignment;
|
|
28
|
+
}
|
|
17
29
|
export declare const HeaderButtonDisplayName = "HeaderButton";
|
|
18
30
|
export declare const HeaderButton: {
|
|
19
|
-
({ id, children, icon, iconOpen,
|
|
31
|
+
({ id, children, icon, iconOpen, iconPosition, className, important, onClick, inContextMenu, disabledTooltip, tooltipAlignment, disabled, link, ...restProps }: IHeaderButton & IHeaderButtonInternal): import("react/jsx-runtime").JSX.Element;
|
|
20
32
|
displayName: string;
|
|
21
33
|
};
|
|
34
|
+
export {};
|
|
@@ -13,13 +13,18 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
13
13
|
import { Button } from "../button/button";
|
|
14
14
|
import { ButtonIconPosition, TextIconButton } from "../textIconButton/textIconButton";
|
|
15
15
|
import { classNames } from "../commonHelpers/classNames/classNames";
|
|
16
|
+
import { Tooltip } from "../tooltip/tooltip";
|
|
17
|
+
import { getButtonDisabledProps } from "../button/buttonDisabledProps";
|
|
16
18
|
export const HeaderButtonDisplayName = "HeaderButton";
|
|
17
19
|
export const HeaderButton = (_a) => {
|
|
18
|
-
var { id, children, icon, iconOpen,
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
var { id, children, icon, iconOpen, iconPosition = ButtonIconPosition.Start, className = "", important, onClick, inContextMenu, disabledTooltip, tooltipAlignment = "bottom", disabled, link } = _a, restProps = __rest(_a, ["id", "children", "icon", "iconOpen", "iconPosition", "className", "important", "onClick", "inContextMenu", "disabledTooltip", "tooltipAlignment", "disabled", "link"]);
|
|
21
|
+
const tooltip = disabled ? disabledTooltip : undefined;
|
|
22
|
+
// Native `disabled` takes a button out of the tab order and blocks hover, so a tooltip could never be reached.
|
|
23
|
+
// Only a disabled button that has a tooltip to show is kept focusable, via `aria-disabled`.
|
|
24
|
+
const _b = getButtonDisabledProps({ disabled, focusable: Boolean(tooltip), link }), { className: disabledClassName } = _b, disabledProps = __rest(_b, ["className"]);
|
|
25
|
+
const fullClassName = classNames(["zen-header-button", className, disabledClassName !== null && disabledClassName !== void 0 ? disabledClassName : ""]);
|
|
26
|
+
const props = Object.assign(Object.assign(Object.assign({}, restProps), { link, "data-important": important ? "1" : "0" }), disabledProps);
|
|
27
|
+
const button = icon ? (_jsx(TextIconButton, Object.assign({ id: id, icon: icon, iconPosition: iconPosition, className: fullClassName, onClick: onClick ? e => onClick(id, e) : undefined }, props, { children: children }))) : (_jsx(Button, Object.assign({ id: id, className: fullClassName, onClick: onClick ? e => onClick(id, e) : undefined }, props, { children: children })));
|
|
28
|
+
return tooltip ? (_jsx(Tooltip, { trigger: button, alignment: tooltipAlignment, children: tooltip })) : (button);
|
|
24
29
|
};
|
|
25
30
|
HeaderButton.displayName = HeaderButtonDisplayName;
|
|
@@ -10,60 +10,6 @@ import { useDrive } from "../../utils/theme/useDrive";
|
|
|
10
10
|
import { getMenuButtonState } from "../utils/getMenuButtonState";
|
|
11
11
|
import { isSafeHref } from "../utils/isSafeHref";
|
|
12
12
|
import { getSafeRel } from "../utils/getSafeRel";
|
|
13
|
-
// Templates below drop native `disabled` to stay focusable/hoverable; this guard blocks activation instead.
|
|
14
|
-
const handleDisabledGuardedClick = (e, disabled, id, onClick) => {
|
|
15
|
-
if (disabled) {
|
|
16
|
-
e.preventDefault();
|
|
17
|
-
e.stopPropagation();
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
onClick === null || onClick === void 0 ? void 0 : onClick(id, e);
|
|
21
|
-
};
|
|
22
|
-
// Skip Space-to-activate when disabled, so it doesn't flash `--active` or self-click via `linkEl.click()`.
|
|
23
|
-
const createDisabledGuardedLinkKeyEvents = (disabled) => ({
|
|
24
|
-
onKeyDown: (e) => {
|
|
25
|
-
if (disabled || e.key !== " ") {
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
e.preventDefault();
|
|
29
|
-
const linkEl = e.target;
|
|
30
|
-
linkEl.dataset.spaceDown = "1";
|
|
31
|
-
linkEl.classList.add("zen-menu-button__action--active");
|
|
32
|
-
},
|
|
33
|
-
onKeyUp: (e) => {
|
|
34
|
-
if (disabled || e.key !== " ") {
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
e.preventDefault();
|
|
38
|
-
const linkEl = e.target;
|
|
39
|
-
linkEl.classList.remove("zen-menu-button__action--active");
|
|
40
|
-
if (linkEl.dataset.spaceDown) {
|
|
41
|
-
delete linkEl.dataset.spaceDown;
|
|
42
|
-
linkEl.click();
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
const getMenuButtonActionClassName = (hasChildren, hasState, isActive, disabled, destructiveClass, driveClasses) => classNames([
|
|
47
|
-
"zen-menu-button__action",
|
|
48
|
-
"zen-button",
|
|
49
|
-
"zen-caption",
|
|
50
|
-
hasChildren ? "zen-menu-button__action--has-children" : "",
|
|
51
|
-
hasState && isActive ? "zen-menu-button__action--active" : "",
|
|
52
|
-
disabled ? "zen-menu-button__action--disabled" : "",
|
|
53
|
-
disabled ? "zen-button--disabled" : "",
|
|
54
|
-
disabled ? "zen-button--disabled-focusable" : "",
|
|
55
|
-
destructiveClass,
|
|
56
|
-
driveClasses || ""
|
|
57
|
-
]);
|
|
58
|
-
const getMenuLinkActionClassName = (disabled, destructiveClass, driveClasses) => classNames(["zen-menu-button__action", "zen-caption", disabled ? "zen-menu-button__action--disabled" : "", destructiveClass, driveClasses || ""]);
|
|
59
|
-
// An <a> without `href` isn't tabbable, so `tabIndex="0"` keeps a disabled link item in the tab order.
|
|
60
|
-
const getMenuLinkAccessibilityProps = (disabled, link) => ({
|
|
61
|
-
href: disabled || !isSafeHref(link) ? undefined : link,
|
|
62
|
-
tabIndex: disabled ? 0 : undefined,
|
|
63
|
-
"aria-disabled": disabled
|
|
64
|
-
});
|
|
65
|
-
const getMenuItemRole = (hasState) => (hasState ? "menuitemcheckbox" : "menuitem");
|
|
66
|
-
const getMenuItemAriaChecked = (hasState, isActive) => (hasState ? isActive : undefined);
|
|
67
13
|
const createMenuIcon = (iconComponent, size, className) => iconComponent ? createElement(iconComponent, { size, className }) : null;
|
|
68
14
|
const resolveTarget = (target, iconOpen) => target !== null && target !== void 0 ? target : (iconOpen ? "_blank" : undefined);
|
|
69
15
|
export const MenuButton = ({ id, onClick, hasChildren, disabled, icon, iconOpen, destructive, name, link, target, rel, className = "", active = null, ref, dataAnalyticsId }) => {
|
|
@@ -74,10 +20,46 @@ export const MenuButton = ({ id, onClick, hasChildren, disabled, icon, iconOpen,
|
|
|
74
20
|
const destructiveClass = destructive ? "zen-menu-button__action--destructive" : "";
|
|
75
21
|
const effectiveTarget = resolveTarget(target, iconOpen);
|
|
76
22
|
const ChevronIcon = useDirectionalIcon(IconChevronRight, IconChevronLeft);
|
|
77
|
-
const linkEvents = useMemo(() =>
|
|
78
|
-
|
|
23
|
+
const linkEvents = useMemo(() => ({
|
|
24
|
+
onKeyDown: e => {
|
|
25
|
+
if (e.key === " ") {
|
|
26
|
+
e.preventDefault();
|
|
27
|
+
const linkEl = e.target;
|
|
28
|
+
linkEl.dataset.spaceDown = "1";
|
|
29
|
+
linkEl.classList.add("zen-menu-button__action--active");
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
onKeyUp: e => {
|
|
33
|
+
if (e.key === " ") {
|
|
34
|
+
e.preventDefault();
|
|
35
|
+
const linkEl = e.target;
|
|
36
|
+
linkEl.classList.remove("zen-menu-button__action--active");
|
|
37
|
+
if (linkEl.dataset.spaceDown) {
|
|
38
|
+
delete linkEl.dataset.spaceDown;
|
|
39
|
+
linkEl.click();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}), []);
|
|
44
|
+
const onClickHandler = useCallback((e) => {
|
|
45
|
+
onClick === null || onClick === void 0 ? void 0 : onClick(id, e);
|
|
46
|
+
}, [id, onClick]);
|
|
79
47
|
const iconAfterElement = createMenuIcon(iconOpen ? IconOpen : undefined, iconSize, "zen-menu-button__icon-after");
|
|
80
|
-
const buttonTemplate = (_jsxs("button", { type: "button", "data-id": id, ref: ref,
|
|
81
|
-
|
|
48
|
+
const buttonTemplate = (_jsxs("button", { type: "button", "data-id": id, ref: ref, disabled: !!disabled, title: name, className: classNames([
|
|
49
|
+
"zen-menu-button__action",
|
|
50
|
+
"zen-button",
|
|
51
|
+
"zen-caption",
|
|
52
|
+
hasChildren ? "zen-menu-button__action--has-children" : "",
|
|
53
|
+
hasState && isActive ? "zen-menu-button__action--active" : "",
|
|
54
|
+
destructiveClass,
|
|
55
|
+
driveMenuButtonActionClasses || ""
|
|
56
|
+
]), role: hasState ? "menuitemcheckbox" : "menuitem", onClick: onClickHandler, "aria-checked": hasState ? isActive : undefined, "data-analytics-id": dataAnalyticsId, children: [createMenuIcon(icon, iconSize, classNames(["zen-caption__pre-content", "zen-menu-button__icon"])), _jsx("span", { className: "zen-menu-button__action-text", children: name }), iconAfterElement, !!hasChildren && _jsx(ChevronIcon, { size: iconSize, className: classNames(["zen-caption__pre-content", "zen-menu-button__more-icon"]) })] }));
|
|
57
|
+
const linkTemplate = (_jsxs("a", Object.assign({ "data-id": id, role: "menuitem", className: classNames([
|
|
58
|
+
"zen-menu-button__action",
|
|
59
|
+
"zen-caption",
|
|
60
|
+
disabled ? "zen-menu-button__action--disabled" : "",
|
|
61
|
+
destructiveClass,
|
|
62
|
+
driveMenuButtonActionClasses || ""
|
|
63
|
+
]), href: disabled || !isSafeHref(link) ? undefined : link, "aria-disabled": disabled, onClick: onClickHandler, target: effectiveTarget, rel: getSafeRel(rel, effectiveTarget), "data-analytics-id": dataAnalyticsId }, linkEvents, { children: [createMenuIcon(icon, iconSize, "zen-caption__pre-content"), iconAfterElement ? _jsx("span", { className: "zen-menu-button__action-text", children: name }) : name, iconAfterElement] })));
|
|
82
64
|
return (_jsx("li", { role: "presentation", className: classNames(["zen-menu-button", className]), children: link ? linkTemplate : buttonTemplate }));
|
|
83
65
|
};
|