@astryxdesign/core 0.7.5-canary.c60f571 → 0.7.5-canary.cd53c2d
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/dist/DateInput/Wheel.d.ts.map +1 -1
- package/dist/DateInput/Wheel.js +1 -1
- package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +10 -5
- package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuRadioItem.js +13 -5
- package/dist/Field/inputDisabledFocus.d.ts +14 -0
- package/dist/Field/inputDisabledFocus.d.ts.map +1 -0
- package/dist/Field/inputDisabledFocus.js +19 -0
- package/dist/Layer/useTouchTrigger.d.ts +14 -1
- package/dist/Layer/useTouchTrigger.d.ts.map +1 -1
- package/dist/Layer/useTouchTrigger.js +72 -17
- package/dist/NumberInput/NumberInput.d.ts +3 -3
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +46 -20
- package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
- package/dist/SideNav/SideNavHeading.js +34 -0
- package/dist/Skeleton/Skeleton.d.ts.map +1 -1
- package/dist/Skeleton/Skeleton.js +12 -3
- package/dist/TextArea/TextArea.d.ts +6 -4
- package/dist/TextArea/TextArea.d.ts.map +1 -1
- package/dist/TextArea/TextArea.js +14 -11
- package/dist/TextInput/TextInput.d.ts +6 -4
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +27 -17
- package/dist/astryx.css +2 -3
- package/package.json +3 -3
- package/src/DateInput/DateInputTouch.test.tsx +9 -0
- package/src/DateInput/Wheel.tsx +4 -1
- package/src/DateInput/__tests__/WheelPinchZoom.a11y.chromium.spec.ts +158 -0
- package/src/DropdownMenu/DropdownMenuRadioItem.doc.mjs +5 -2
- package/src/DropdownMenu/DropdownMenuRadioItem.spec.md +226 -0
- package/src/DropdownMenu/DropdownMenuRadioItem.tsx +32 -16
- package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +158 -0
- package/src/DropdownMenu/__tests__/DropdownMenuRadioItemAudit.a11y.chromium.spec.ts +539 -0
- package/src/Field/inputDisabledFocus.ts +28 -0
- package/src/HoverCard/HoverCard.test.tsx +93 -3
- package/src/Layer/__tests__/TouchTrigger.a11y.chromium.spec.ts +199 -0
- package/src/Layer/useTouchTrigger.ts +95 -41
- package/src/NumberInput/NumberInput.doc.mjs +6 -6
- package/src/NumberInput/NumberInput.test.tsx +144 -14
- package/src/NumberInput/NumberInput.tsx +68 -21
- package/src/Pagination/Pagination.test.tsx +2 -2
- package/src/SideNav/SideNav.test.tsx +15 -6
- package/src/SideNav/SideNavHeading.tsx +41 -0
- package/src/Skeleton/Skeleton.test.tsx +108 -1
- package/src/Skeleton/Skeleton.tsx +30 -2
- package/src/TextArea/TextArea.doc.mjs +6 -6
- package/src/TextArea/TextArea.test.tsx +65 -6
- package/src/TextArea/TextArea.tsx +26 -14
- package/src/TextInput/TextInput.doc.mjs +8 -8
- package/src/TextInput/TextInput.test.tsx +83 -10
- package/src/TextInput/TextInput.tsx +38 -19
- package/src/Tooltip/Tooltip.test.tsx +148 -3
- package/src/Tooltip/__tests__/Tooltip.a11y.states.ts +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Wheel.d.ts","sourceRoot":"","sources":["../../src/DateInput/Wheel.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Wheel.d.ts","sourceRoot":"","sources":["../../src/DateInput/Wheel.tsx"],"names":[],"mappings":"AA8MA,MAAM,WAAW,WAAW;IAC1B,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,iEAAiE;IACjE,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd,2BAA2B;IAC3B,OAAO,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IACpC,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,sEAAsE;IACtE,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,KAAK,CAAC,EACpB,KAAK,EACL,OAAO,EACP,KAAK,EACL,QAAQ,EACR,QAAe,GAChB,EAAE,UAAU,+BAgOZ;yBAtOe,KAAK"}
|
package/dist/DateInput/Wheel.js
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
* (role="menuitemradio"). The row owns the role and aria-checked; there is no
|
|
9
9
|
* nested native <input> (per the WAI-ARIA menuitemradio pattern). Selection
|
|
10
10
|
* state + onChange come from DropdownMenuRadioGroupContext. Keyboard nav +
|
|
11
|
-
* Enter/Space activation come from the parent DropdownMenu.
|
|
11
|
+
* Enter/Space activation come from the parent DropdownMenu. Caller onClick and
|
|
12
|
+
* onPointerMove run after the row's own selection and hover focus.
|
|
12
13
|
*
|
|
13
14
|
* The round radio visual is the shared radio indicator, decorative
|
|
14
15
|
* (aria-hidden) — the row owns the checked state, and menu radios pick up the
|
|
@@ -39,9 +40,9 @@ export interface DropdownMenuRadioItemProps extends Omit<BaseProps, 'role' | 'ar
|
|
|
39
40
|
*/
|
|
40
41
|
icon?: ReactNode | IconType;
|
|
41
42
|
/**
|
|
42
|
-
* Whether this individual radio item is disabled. Disabled items
|
|
43
|
-
*
|
|
44
|
-
*
|
|
43
|
+
* Whether this individual radio item is disabled. Disabled items expose
|
|
44
|
+
* `aria-disabled`, block selection, and are currently skipped by
|
|
45
|
+
* DropdownMenu arrow-key navigation.
|
|
45
46
|
* @default false
|
|
46
47
|
*/
|
|
47
48
|
isDisabled?: boolean;
|
|
@@ -56,6 +57,10 @@ export interface DropdownMenuRadioItemProps extends Omit<BaseProps, 'role' | 'ar
|
|
|
56
57
|
/**
|
|
57
58
|
* A single option in a DropdownMenuRadioGroup (role="menuitemradio").
|
|
58
59
|
*
|
|
60
|
+
* Consumer click and pointer-move handlers compose after the row's own
|
|
61
|
+
* selection and hover-focus behavior; disabled activation stops before the
|
|
62
|
+
* consumer click handler.
|
|
63
|
+
*
|
|
59
64
|
* @example
|
|
60
65
|
* ```
|
|
61
66
|
* <DropdownMenuRadioGroup value={sort} onChange={setSort} label="Sort by">
|
|
@@ -64,7 +69,7 @@ export interface DropdownMenuRadioItemProps extends Omit<BaseProps, 'role' | 'ar
|
|
|
64
69
|
* </DropdownMenuRadioGroup>
|
|
65
70
|
* ```
|
|
66
71
|
*/
|
|
67
|
-
export declare function DropdownMenuRadioItem({ value, label, description, icon, isDisabled, endContent, xstyle, className, style, ...rest }: DropdownMenuRadioItemProps): import("react").JSX.Element;
|
|
72
|
+
export declare function DropdownMenuRadioItem({ value, label, description, icon, isDisabled, endContent, xstyle, className, style, onClick, onPointerMove, ...rest }: DropdownMenuRadioItemProps): import("react").JSX.Element;
|
|
68
73
|
export declare namespace DropdownMenuRadioItem {
|
|
69
74
|
var displayName: string;
|
|
70
75
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DropdownMenuRadioItem.d.ts","sourceRoot":"","sources":["../../src/DropdownMenu/DropdownMenuRadioItem.tsx"],"names":[],"mappings":"AAIA
|
|
1
|
+
{"version":3,"file":"DropdownMenuRadioItem.d.ts","sourceRoot":"","sources":["../../src/DropdownMenu/DropdownMenuRadioItem.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAIL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,EAAiB,KAAK,QAAQ,EAAC,MAAM,SAAS,CAAC;AAOtD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AA4C5C,MAAM,WAAW,0BAA2B,SAAQ,IAAI,CACtD,SAAS,EACT,MAAM,GAAG,cAAc,GAAG,UAAU,CACrC;IACC;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,KAAK,EAAE,SAAS,CAAC;IACjB;;OAEG;IACH,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB;;;OAGG;IACH,IAAI,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC5B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,mFAAmF;IACnF,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;CAC9B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,KAAK,EACL,KAAK,EACL,WAAW,EACX,IAAI,EACJ,UAAkB,EAClB,UAAU,EACV,MAAM,EACN,SAAS,EACT,KAAK,EACL,OAAO,EACP,aAAa,EACb,GAAG,IAAI,EACR,EAAE,0BAA0B,+BAuE5B;yBApFe,qBAAqB"}
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
* (role="menuitemradio"). The row owns the role and aria-checked; there is no
|
|
13
13
|
* nested native <input> (per the WAI-ARIA menuitemradio pattern). Selection
|
|
14
14
|
* state + onChange come from DropdownMenuRadioGroupContext. Keyboard nav +
|
|
15
|
-
* Enter/Space activation come from the parent DropdownMenu.
|
|
15
|
+
* Enter/Space activation come from the parent DropdownMenu. Caller onClick and
|
|
16
|
+
* onPointerMove run after the row's own selection and hover focus.
|
|
16
17
|
*
|
|
17
18
|
* The round radio visual is the shared radio indicator, decorative
|
|
18
19
|
* (aria-hidden) — the row owns the checked state, and menu radios pick up the
|
|
@@ -29,7 +30,7 @@ import { useDropdownMenuContext } from "./DropdownMenuContext.js";
|
|
|
29
30
|
import { focusMenuItemOnHover } from "./menuItemHover.js";
|
|
30
31
|
import "../theme/tokens.stylex.js";
|
|
31
32
|
import { colorVars, spacingVars } from "../theme/tokens.stylex.js";
|
|
32
|
-
import { mergeProps, themeProps } from "../utils/index.js";
|
|
33
|
+
import { composeEventHandlers, mergeProps, themeProps } from "../utils/index.js";
|
|
33
34
|
import { useDropdownMenuRadioGroupContext } from "./DropdownMenuContext.js";
|
|
34
35
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
35
36
|
const styles = {
|
|
@@ -61,6 +62,10 @@ const styles = {
|
|
|
61
62
|
/**
|
|
62
63
|
* A single option in a DropdownMenuRadioGroup (role="menuitemradio").
|
|
63
64
|
*
|
|
65
|
+
* Consumer click and pointer-move handlers compose after the row's own
|
|
66
|
+
* selection and hover-focus behavior; disabled activation stops before the
|
|
67
|
+
* consumer click handler.
|
|
68
|
+
*
|
|
64
69
|
* @example
|
|
65
70
|
* ```
|
|
66
71
|
* <DropdownMenuRadioGroup value={sort} onChange={setSort} label="Sort by">
|
|
@@ -79,6 +84,8 @@ export function DropdownMenuRadioItem({
|
|
|
79
84
|
xstyle,
|
|
80
85
|
className,
|
|
81
86
|
style,
|
|
87
|
+
onClick,
|
|
88
|
+
onPointerMove,
|
|
82
89
|
...rest
|
|
83
90
|
}) {
|
|
84
91
|
const menuCtx = useDropdownMenuContext();
|
|
@@ -90,7 +97,7 @@ export function DropdownMenuRadioItem({
|
|
|
90
97
|
const controlSize = menuSize === 'sm' ? 'sm' : 'md';
|
|
91
98
|
const isChecked = groupCtx.value === value;
|
|
92
99
|
const RadioControl = useIndicator('radio');
|
|
93
|
-
const handleClick = useCallback(
|
|
100
|
+
const handleClick = useCallback(event => {
|
|
94
101
|
if (isDisabled) {
|
|
95
102
|
return;
|
|
96
103
|
}
|
|
@@ -98,14 +105,15 @@ export function DropdownMenuRadioItem({
|
|
|
98
105
|
if (groupCtx.hasCloseOnSelect) {
|
|
99
106
|
menuCtx?.closeMenu();
|
|
100
107
|
}
|
|
101
|
-
|
|
108
|
+
onClick?.(event);
|
|
109
|
+
}, [isDisabled, groupCtx, value, menuCtx, onClick]);
|
|
102
110
|
const handlePointerMove = useCallback(e => focusMenuItemOnHover(e, isDisabled), [isDisabled]);
|
|
103
111
|
return /*#__PURE__*/_jsx(Item, {
|
|
104
112
|
...rest,
|
|
105
113
|
role: "menuitemradio",
|
|
106
114
|
"aria-checked": isChecked,
|
|
107
115
|
tabIndex: isDisabled ? undefined : -1,
|
|
108
|
-
onPointerMove: handlePointerMove,
|
|
116
|
+
onPointerMove: composeEventHandlers(handlePointerMove, onPointerMove),
|
|
109
117
|
marker:
|
|
110
118
|
/*#__PURE__*/
|
|
111
119
|
// No wrapper — see DropdownMenuCheckboxItem: the target belongs on the
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file inputDisabledFocus.ts
|
|
3
|
+
* @input Uses StyleX and theme tokens
|
|
4
|
+
* @output inputDisabledFocusStyles (internal; not re-exported from Field/index.ts)
|
|
5
|
+
* @position Field wrapper style shared by TextInput and TextArea
|
|
6
|
+
*/
|
|
7
|
+
import * as stylex from '@stylexjs/stylex';
|
|
8
|
+
export declare const inputDisabledFocusStyles: Readonly<{
|
|
9
|
+
readonly ring: Readonly<{
|
|
10
|
+
readonly borderColor: stylex.StyleXClassNameFor<"borderColor", "light-dark(#0064E0, #2694FE)" | "light-dark(#CCD3DB, #494D53)">;
|
|
11
|
+
readonly boxShadow: stylex.StyleXClassNameFor<"boxShadow", "none" | `inset 0px 0px 0px 2px ${stylex.StyleXVar<"light-dark(#0082FB33, #0082FB3F)">}`>;
|
|
12
|
+
}>;
|
|
13
|
+
}>;
|
|
14
|
+
//# sourceMappingURL=inputDisabledFocus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inputDisabledFocus.d.ts","sourceRoot":"","sources":["../../src/Field/inputDisabledFocus.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,OAAO,KAAK,MAAM,MAAM,kBAAkB,CAAC;AAG3C,eAAO,MAAM,wBAAwB;;;;;EAenC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file inputDisabledFocus.ts
|
|
5
|
+
* @input Uses StyleX and theme tokens
|
|
6
|
+
* @output inputDisabledFocusStyles (internal; not re-exported from Field/index.ts)
|
|
7
|
+
* @position Field wrapper style shared by TextInput and TextArea
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import * as stylex from '@stylexjs/stylex';
|
|
11
|
+
import "../theme/tokens.stylex.js";
|
|
12
|
+
import { colorVars } from "../theme/tokens.stylex.js";
|
|
13
|
+
export const inputDisabledFocusStyles = {
|
|
14
|
+
ring: {
|
|
15
|
+
kVAM5u: "xvy26l8 x16it14f",
|
|
16
|
+
kGVxlE: "x1gnnqk1 xwmge8v",
|
|
17
|
+
$$css: true
|
|
18
|
+
}
|
|
19
|
+
};
|
|
@@ -20,6 +20,17 @@
|
|
|
20
20
|
* lose, so the tap opens the layer and the next tap outside dismisses it. That
|
|
21
21
|
* is `auto`; `tap` and `none` state the choice outright, which is what an
|
|
22
22
|
* icon-button whose only job is to reveal the layer needs.
|
|
23
|
+
*
|
|
24
|
+
* A touch decides nothing until it is a tap. A finger landing on the trigger
|
|
25
|
+
* may be starting a scroll, a pinch or a long press, and only the browser
|
|
26
|
+
* knows which once the finger moves or lifts. Its `click` is that verdict (on
|
|
27
|
+
* iOS it comes from UIKit's own tap recognizer), so the press only records
|
|
28
|
+
* itself and the tap's `click` opens or closes the layer; a press the browser
|
|
29
|
+
* takes for scrolling ends in `pointercancel` and never gets there. That is
|
|
30
|
+
* how a UIKit tap behaves: it recognizes when the finger lifts and fails once
|
|
31
|
+
* the scroll view claims the touch. The tap outside that closes a tap-opened
|
|
32
|
+
* layer counts when the finger lifts too, or when a scroll takes the touch,
|
|
33
|
+
* which is when UIKit dismisses a popover.
|
|
23
34
|
*/
|
|
24
35
|
import { type RefObject } from 'react';
|
|
25
36
|
/**
|
|
@@ -86,7 +97,9 @@ export interface UseTouchTriggerReturn {
|
|
|
86
97
|
/**
|
|
87
98
|
* Attach to the trigger. Returns true when the press was a touch one and
|
|
88
99
|
* this hook has dealt with it, meaning the caller's own pointer-down
|
|
89
|
-
* behavior must not also run.
|
|
100
|
+
* behavior must not also run. A touch press opens or closes nothing by
|
|
101
|
+
* itself: the hook waits for that press's tap (the `click` the browser
|
|
102
|
+
* fires when the finger lifts without scrolling) on the same element.
|
|
90
103
|
*/
|
|
91
104
|
handlePointerDown: (event: PointerEvent) => boolean;
|
|
92
105
|
/** Forget a tap-open. Call from every other close path (Escape, controlled). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTouchTrigger.d.ts","sourceRoot":"","sources":["../../src/Layer/useTouchTrigger.ts"],"names":[],"mappings":"AAIA
|
|
1
|
+
{"version":3,"file":"useTouchTrigger.d.ts","sourceRoot":"","sources":["../../src/Layer/useTouchTrigger.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAiC,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAOrE;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;AAmCxD;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAoB7D;AAeD,MAAM,WAAW,sBAAsB;IACrC,sDAAsD;IACtD,YAAY,EAAE,iBAAiB,CAAC;IAEhC,oDAAoD;IACpD,SAAS,EAAE,OAAO,CAAC;IAEnB;;;OAGG;IACH,YAAY,EAAE,OAAO,CAAC;IAEtB,2CAA2C;IAC3C,MAAM,EAAE,OAAO,CAAC;IAEhB,0EAA0E;IAC1E,OAAO,EAAE,MAAM,CAAC;IAEhB,oDAAoD;IACpD,UAAU,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAE1C,uDAAuD;IACvD,IAAI,EAAE,MAAM,IAAI,CAAC;IAEjB,mCAAmC;IACnC,IAAI,EAAE,MAAM,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,iBAAiB,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;IAEtC;;;;OAIG;IACH,kBAAkB,EAAE,MAAM,OAAO,CAAC;IAElC;;;;OAIG;IACH,kBAAkB,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IAElD;;;;;;OAMG;IACH,iBAAiB,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC;IAEpD,gFAAgF;IAChF,YAAY,EAAE,MAAM,IAAI,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,sBAAsB,GAC9B,qBAAqB,CAuMvB"}
|
|
@@ -24,8 +24,20 @@
|
|
|
24
24
|
* lose, so the tap opens the layer and the next tap outside dismisses it. That
|
|
25
25
|
* is `auto`; `tap` and `none` state the choice outright, which is what an
|
|
26
26
|
* icon-button whose only job is to reveal the layer needs.
|
|
27
|
+
*
|
|
28
|
+
* A touch decides nothing until it is a tap. A finger landing on the trigger
|
|
29
|
+
* may be starting a scroll, a pinch or a long press, and only the browser
|
|
30
|
+
* knows which once the finger moves or lifts. Its `click` is that verdict (on
|
|
31
|
+
* iOS it comes from UIKit's own tap recognizer), so the press only records
|
|
32
|
+
* itself and the tap's `click` opens or closes the layer; a press the browser
|
|
33
|
+
* takes for scrolling ends in `pointercancel` and never gets there. That is
|
|
34
|
+
* how a UIKit tap behaves: it recognizes when the finger lifts and fails once
|
|
35
|
+
* the scroll view claims the touch. The tap outside that closes a tap-opened
|
|
36
|
+
* layer counts when the finger lifts too, or when a scroll takes the touch,
|
|
37
|
+
* which is when UIKit dismisses a popover.
|
|
27
38
|
*/
|
|
28
39
|
import { useCallback, useEffect, useRef } from 'react';
|
|
40
|
+
import { currentGesture } from "./gestureCounter.js";
|
|
29
41
|
import { getInteractionModality, useInteractionModalityTracking } from "../utils/interactionModality.js";
|
|
30
42
|
|
|
31
43
|
/**
|
|
@@ -144,6 +156,9 @@ export function useTouchTrigger(options) {
|
|
|
144
156
|
// pointer-leave, and a controlled one is not ours to close.
|
|
145
157
|
const isTapOpenRef = useRef(false);
|
|
146
158
|
const outsideListenerRef = useRef(null);
|
|
159
|
+
// A touch press on the trigger still waiting for its tap: calling it stops
|
|
160
|
+
// waiting. The next press, a cancel, the tap itself or unmounting calls it.
|
|
161
|
+
const forgetPendingTapRef = useRef(null);
|
|
147
162
|
useInteractionModalityTracking();
|
|
148
163
|
const disarmOutsideDismiss = useCallback(() => {
|
|
149
164
|
isTapOpenRef.current = false;
|
|
@@ -152,22 +167,26 @@ export function useTouchTrigger(options) {
|
|
|
152
167
|
return;
|
|
153
168
|
}
|
|
154
169
|
outsideListenerRef.current = null;
|
|
155
|
-
document.removeEventListener('
|
|
170
|
+
document.removeEventListener('pointerup', listener, true);
|
|
171
|
+
document.removeEventListener('pointercancel', listener, true);
|
|
156
172
|
}, []);
|
|
157
173
|
|
|
158
174
|
// Capture phase, so a trigger that stops propagation cannot strand an open
|
|
159
|
-
// layer.
|
|
160
|
-
//
|
|
175
|
+
// layer. It is armed by the opening tap's `click`, after that tap's own
|
|
176
|
+
// `pointerup`, so the listener never sees the tap that opened the layer.
|
|
177
|
+
// A tap outside counts when it lifts (`pointerup`); a scroll or pinch that
|
|
178
|
+
// starts outside counts as soon as the browser takes it (`pointercancel`).
|
|
161
179
|
const armOutsideDismiss = useCallback(() => {
|
|
162
180
|
isTapOpenRef.current = true;
|
|
163
181
|
if (outsideListenerRef.current != null) {
|
|
164
182
|
return;
|
|
165
183
|
}
|
|
166
|
-
const
|
|
184
|
+
const handleOutsidePointerEnd = event => {
|
|
167
185
|
const target = event.target;
|
|
168
186
|
if (target != null) {
|
|
169
|
-
//
|
|
170
|
-
|
|
187
|
+
// A tap on the trigger is the trigger's to toggle. A scroll that
|
|
188
|
+
// starts there is a scroll outside the layer, and it does close it.
|
|
189
|
+
if (event.type === 'pointerup' && triggerRef.current?.contains(target) === true) {
|
|
171
190
|
return;
|
|
172
191
|
}
|
|
173
192
|
// Hover card content is interactive; a tap inside it is not a dismiss.
|
|
@@ -178,10 +197,12 @@ export function useTouchTrigger(options) {
|
|
|
178
197
|
disarmOutsideDismiss();
|
|
179
198
|
hideRef.current();
|
|
180
199
|
};
|
|
181
|
-
outsideListenerRef.current =
|
|
182
|
-
document.addEventListener('
|
|
200
|
+
outsideListenerRef.current = handleOutsidePointerEnd;
|
|
201
|
+
document.addEventListener('pointerup', handleOutsidePointerEnd, true);
|
|
202
|
+
document.addEventListener('pointercancel', handleOutsidePointerEnd, true);
|
|
183
203
|
}, [triggerRef, disarmOutsideDismiss]);
|
|
184
204
|
useEffect(() => disarmOutsideDismiss, [disarmOutsideDismiss]);
|
|
205
|
+
useEffect(() => () => forgetPendingTapRef.current?.(), []);
|
|
185
206
|
const isTouchInteraction = useCallback(() => isTouchPointerRef.current && getInteractionModality() === 'pointer', []);
|
|
186
207
|
const handlePointerEnter = useCallback(event => {
|
|
187
208
|
// Only a finger is hoverless on arrival. A pen in detection range hovers
|
|
@@ -190,19 +211,18 @@ export function useTouchTrigger(options) {
|
|
|
190
211
|
// presses is a tap, and `handlePointerDown` still reads it as one.
|
|
191
212
|
isTouchPointerRef.current = event.pointerType === 'touch';
|
|
192
213
|
}, []);
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
214
|
+
|
|
215
|
+
// What a tap on the trigger does. It runs from the tap's `click`, after the
|
|
216
|
+
// render that armed it, so it reads the latest props through this ref.
|
|
217
|
+
const answerTapRef = useRef(() => {});
|
|
218
|
+
answerTapRef.current = () => {
|
|
199
219
|
const trigger = triggerRef.current;
|
|
200
220
|
const mode = touchTrigger === 'auto' ? trigger != null && isActionTrigger(trigger) ? 'none' : 'tap' : touchTrigger;
|
|
201
221
|
if (mode === 'none' || !isEnabled) {
|
|
202
222
|
// A layer left open by an earlier tap must not survive the next one.
|
|
203
223
|
disarmOutsideDismiss();
|
|
204
224
|
hide();
|
|
205
|
-
return
|
|
225
|
+
return;
|
|
206
226
|
}
|
|
207
227
|
|
|
208
228
|
// A lazily mounted layer reports `isOpen` a tick after `show()`, so the
|
|
@@ -210,12 +230,47 @@ export function useTouchTrigger(options) {
|
|
|
210
230
|
if (isOpenRef.current || isTapOpenRef.current) {
|
|
211
231
|
disarmOutsideDismiss();
|
|
212
232
|
hide();
|
|
213
|
-
return
|
|
233
|
+
return;
|
|
214
234
|
}
|
|
215
235
|
armOutsideDismiss();
|
|
216
236
|
show();
|
|
237
|
+
};
|
|
238
|
+
const handlePointerDown = useCallback(event => {
|
|
239
|
+
const isTouch = TOUCH_POINTER_TYPES.has(event.pointerType);
|
|
240
|
+
isTouchPointerRef.current = isTouch;
|
|
241
|
+
// A new press replaces one that never became a tap.
|
|
242
|
+
forgetPendingTapRef.current?.();
|
|
243
|
+
if (!isTouch || isControlled) {
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
const trigger = event.currentTarget instanceof Element ? event.currentTarget : triggerRef.current;
|
|
247
|
+
if (trigger == null) {
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// The tap must belong to this press. A key press or a new pointer press
|
|
252
|
+
// advances the gesture, so a keyboard activation that later fires
|
|
253
|
+
// `click` on the trigger (Enter on a button) is not mistaken for it.
|
|
254
|
+
const gesture = currentGesture();
|
|
255
|
+
const forget = () => {
|
|
256
|
+
trigger.removeEventListener('click', handleTap);
|
|
257
|
+
trigger.removeEventListener('pointercancel', forget);
|
|
258
|
+
if (forgetPendingTapRef.current === forget) {
|
|
259
|
+
forgetPendingTapRef.current = null;
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
const handleTap = () => {
|
|
263
|
+
forget();
|
|
264
|
+
if (currentGesture() === gesture) {
|
|
265
|
+
answerTapRef.current();
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
trigger.addEventListener('click', handleTap);
|
|
269
|
+
// The browser took the touch for a scroll or a pinch: no tap is coming.
|
|
270
|
+
trigger.addEventListener('pointercancel', forget);
|
|
271
|
+
forgetPendingTapRef.current = forget;
|
|
217
272
|
return true;
|
|
218
|
-
}, [
|
|
273
|
+
}, [isControlled, triggerRef]);
|
|
219
274
|
|
|
220
275
|
// A layer closed by any other path (Escape, a consumer, the browser) leaves
|
|
221
276
|
// no tap-open to dismiss. Only a true open→closed transition counts: a
|
|
@@ -67,7 +67,7 @@ interface NumberInputPropsBase extends Omit<FieldInputBaseProps, 'onChange' | 'd
|
|
|
67
67
|
* Whether the input is read-only.
|
|
68
68
|
* The value is shown at full opacity and still submits with the form, but
|
|
69
69
|
* cannot be edited. Unlike `isDisabled`, a read-only input is not dimmed and
|
|
70
|
-
*
|
|
70
|
+
* keeps its form value — use it for a value the user should see and send
|
|
71
71
|
* but not change. Stepping is off in every form while read-only: arrow keys,
|
|
72
72
|
* the wheel, and the number steppers. `isDisabled` takes precedence when both
|
|
73
73
|
* are set.
|
|
@@ -81,8 +81,8 @@ interface NumberInputPropsBase extends Omit<FieldInputBaseProps, 'onChange' | 'd
|
|
|
81
81
|
* keyboard and assistive technology. The field cannot be edited (it becomes
|
|
82
82
|
* read-only) while disabled.
|
|
83
83
|
*
|
|
84
|
-
* Use this instead of wrapping a disabled input in `Tooltip
|
|
85
|
-
*
|
|
84
|
+
* Use this instead of wrapping a disabled input in `Tooltip`, so the reason
|
|
85
|
+
* is attached to the field itself.
|
|
86
86
|
*
|
|
87
87
|
* @example
|
|
88
88
|
* ```
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NumberInput.d.ts","sourceRoot":"","sources":["../../src/NumberInput/NumberInput.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAML,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,MAAM,MAAM,kBAAkB,CAAC;AAS3C,OAAO,EAGL,KAAK,WAAW,EAKhB,KAAK,kBAAkB,EACxB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAuB,KAAK,QAAQ,EAAC,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"NumberInput.d.ts","sourceRoot":"","sources":["../../src/NumberInput/NumberInput.tsx"],"names":[],"mappings":"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"}
|
|
@@ -28,6 +28,7 @@ import { useTooltip } from "../Tooltip/index.js";
|
|
|
28
28
|
import { getInputARIA } from "../utils/index.js";
|
|
29
29
|
import { interactionOverlayStyles } from "../utils/interactionOverlay.stylex.js";
|
|
30
30
|
import { usePressFeedback } from "../hooks/usePressFeedback.js";
|
|
31
|
+
import { inputDisabledFocusStyles } from "../Field/inputDisabledFocus.js";
|
|
31
32
|
import { useSize } from "../SizeContext/SizeContext.js";
|
|
32
33
|
import { useInputContainer } from "../hooks/useInputContainer.js";
|
|
33
34
|
import { useInputStatusIcon } from "../hooks/useInputStatusIcon.js";
|
|
@@ -275,12 +276,14 @@ export function NumberInput({
|
|
|
275
276
|
const [pendingInput, setPendingInput] = useState(null);
|
|
276
277
|
const [isFocused, setIsFocused] = useState(false);
|
|
277
278
|
|
|
278
|
-
// Disabled-reason tooltip.
|
|
279
|
-
// tooltip listeners attach to the input container (which already exists) and
|
|
279
|
+
// Disabled-reason tooltip. The tooltip listeners attach to the input container (which already exists) and
|
|
280
280
|
// the input stays perceivable via aria-disabled instead of the native
|
|
281
281
|
// disabled attribute. The field is made read-only so it can't be typed into,
|
|
282
282
|
// and value mutation is blocked by the isDisabled guard in the handlers.
|
|
283
283
|
const showsDisabledMessage = isDisabled && !!disabledMessage;
|
|
284
|
+
// A disabled field keeps its tab stop but, like a natively disabled one,
|
|
285
|
+
// runs no caller event handler other than focus and blur (`spec:AST-085` FR2).
|
|
286
|
+
const fieldProps = isDisabled ? Object.fromEntries(Object.entries(rest).filter(([key]) => !/^on[A-Z]/.test(key) || /^on(Focus|Blur)/.test(key))) : rest;
|
|
284
287
|
const disabledMessageTooltip = useTooltip({
|
|
285
288
|
placement: 'above',
|
|
286
289
|
// The container div is not naturally focusable; focusin bubbles up from
|
|
@@ -359,6 +362,12 @@ export function NumberInput({
|
|
|
359
362
|
if (pendingInput === null) {
|
|
360
363
|
return;
|
|
361
364
|
}
|
|
365
|
+
if (isDisabled) {
|
|
366
|
+
// A field disabled mid-entry commits nothing; its draft is dropped, as
|
|
367
|
+
// a natively disabled field's was (spec:AST-085 FR2).
|
|
368
|
+
setPendingInput(null);
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
362
371
|
const decision = resolveNumberInputCommit(pendingInput, {
|
|
363
372
|
min,
|
|
364
373
|
max,
|
|
@@ -376,7 +385,7 @@ export function NumberInput({
|
|
|
376
385
|
} else if (decision.type === 'commit' && decision.value !== value) {
|
|
377
386
|
onChange(decision.value);
|
|
378
387
|
}
|
|
379
|
-
}, [hasClear, isIntegerOnly, locale, max, min, onChange, pendingInput, value]);
|
|
388
|
+
}, [hasClear, isDisabled, isIntegerOnly, locale, max, min, onChange, pendingInput, value]);
|
|
380
389
|
|
|
381
390
|
// Blur ends the edit and displays the resulting committed value.
|
|
382
391
|
const handleBlur = useCallback(e => {
|
|
@@ -419,6 +428,13 @@ export function NumberInput({
|
|
|
419
428
|
|
|
420
429
|
// Handle keyboard events
|
|
421
430
|
const handleKeyDown = useCallback(e => {
|
|
431
|
+
if (isDisabled) {
|
|
432
|
+
// A read-only field still submits its form on Enter.
|
|
433
|
+
if (e.key === 'Enter') {
|
|
434
|
+
e.preventDefault();
|
|
435
|
+
}
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
422
438
|
// The field is type="text" for formatted display, so an IME can compose
|
|
423
439
|
// into it: Enter commits the candidate and the arrows walk the candidate
|
|
424
440
|
// window. The composing keydown fires before compositionend, so without
|
|
@@ -442,7 +458,7 @@ export function NumberInput({
|
|
|
442
458
|
onEnter?.();
|
|
443
459
|
}
|
|
444
460
|
onKeyDown?.(e);
|
|
445
|
-
}, [commitPendingInput, onEnter, onKeyDown, stepValue]);
|
|
461
|
+
}, [commitPendingInput, isDisabled, onEnter, onKeyDown, stepValue]);
|
|
446
462
|
|
|
447
463
|
// React's delegated wheel listener can be passive, so use a native,
|
|
448
464
|
// explicitly non-passive listener to prevent page scrolling only when this
|
|
@@ -479,12 +495,13 @@ export function NumberInput({
|
|
|
479
495
|
if (!e || e.detail === 0) {
|
|
480
496
|
inputRef.current?.focus();
|
|
481
497
|
} else {
|
|
482
|
-
//
|
|
483
|
-
//
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
498
|
+
// Focus inside the tap: iOS Safari brings up its keyboard only for a
|
|
499
|
+
// focus made while handling the touch, so a field the tap reaches
|
|
500
|
+
// unfocused gets its keyboard, as UIKit's clear button makes the
|
|
501
|
+
// field first responder. `preventScroll` keeps a scrolled page where
|
|
502
|
+
// it is.
|
|
503
|
+
inputRef.current?.focus({
|
|
504
|
+
preventScroll: true
|
|
488
505
|
});
|
|
489
506
|
}
|
|
490
507
|
}, [hasClear, onChange]);
|
|
@@ -513,7 +530,7 @@ export function NumberInput({
|
|
|
513
530
|
status: status?.type ?? null,
|
|
514
531
|
disabled: isDisabled ? 'disabled' : null,
|
|
515
532
|
readonly: isReadOnly ? 'readonly' : null
|
|
516
|
-
}), stylex.props(inputWrapperStyles.base, styles.wrapper, hasNumberSteppers && styles.wrapperWithNumberSteppers, sizeStyles[size], isDisabled && inputWrapperStyles.disabled, status && inputStatusBorderStyles[status.type], status && !isDisabled && inputStatusHoverShadowStyles[status.type], status && inputStatusFocusWithinStyles[status.type], inputGroup && groupStyles.inGroup, controlXstyle), controlClassName, controlStyle),
|
|
533
|
+
}), stylex.props(inputWrapperStyles.base, styles.wrapper, hasNumberSteppers && styles.wrapperWithNumberSteppers, sizeStyles[size], isDisabled && inputWrapperStyles.disabled, isDisabled && inputDisabledFocusStyles.ring, status && inputStatusBorderStyles[status.type], status && !isDisabled && inputStatusHoverShadowStyles[status.type], status && inputStatusFocusWithinStyles[status.type], inputGroup && groupStyles.inGroup, controlXstyle), controlClassName, controlStyle),
|
|
517
534
|
children: [startIcon && renderIconSlot(startIcon, {
|
|
518
535
|
size: 'sm',
|
|
519
536
|
color: 'secondary'
|
|
@@ -521,7 +538,7 @@ export function NumberInput({
|
|
|
521
538
|
id: inputLabelID,
|
|
522
539
|
children: label
|
|
523
540
|
}), /*#__PURE__*/_jsx("input", {
|
|
524
|
-
...
|
|
541
|
+
...fieldProps,
|
|
525
542
|
ref: mergedInputRef,
|
|
526
543
|
id: id,
|
|
527
544
|
name: isDisabled || formatValue ? undefined : htmlName,
|
|
@@ -535,13 +552,12 @@ export function NumberInput({
|
|
|
535
552
|
onBlur: handleBlur,
|
|
536
553
|
onKeyDown: handleKeyDown,
|
|
537
554
|
placeholder: placeholder
|
|
538
|
-
//
|
|
539
|
-
//
|
|
540
|
-
//
|
|
555
|
+
// A disabled input keeps its place in the tab order (spec:AST-085):
|
|
556
|
+
// aria-disabled instead of the native attribute, with readOnly + the
|
|
557
|
+
// handler guards keeping the value from changing.
|
|
541
558
|
,
|
|
542
|
-
disabled: isDisabled
|
|
543
|
-
|
|
544
|
-
readOnly: isReadOnly || showsDisabledMessage || undefined,
|
|
559
|
+
"aria-disabled": isDisabled ? 'true' : undefined,
|
|
560
|
+
readOnly: isReadOnly || isDisabled || undefined,
|
|
545
561
|
autoFocus: hasAutoFocus,
|
|
546
562
|
"data-autofocus": hasAutoFocus || undefined,
|
|
547
563
|
"aria-valuemin": min ?? undefined,
|
|
@@ -597,12 +613,17 @@ export function NumberInput({
|
|
|
597
613
|
type: "button",
|
|
598
614
|
tabIndex: -1,
|
|
599
615
|
...pressable,
|
|
600
|
-
disabled: isDisabled || isReadOnly || !canIncrement,
|
|
616
|
+
"aria-disabled": isDisabled || isReadOnly || !canIncrement || undefined,
|
|
601
617
|
"aria-label": t('@astryx.numberInput.incrementLabel', {
|
|
602
618
|
label
|
|
603
619
|
}),
|
|
604
620
|
onPointerDown: event => event.preventDefault(),
|
|
605
621
|
onClick: () => {
|
|
622
|
+
// An aria-disabled stepper does nothing, as a natively disabled
|
|
623
|
+
// one did (spec:AST-085 FR2).
|
|
624
|
+
if (isDisabled || isReadOnly || !canIncrement) {
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
606
627
|
inputRef.current?.focus();
|
|
607
628
|
stepValue(1);
|
|
608
629
|
},
|
|
@@ -617,12 +638,17 @@ export function NumberInput({
|
|
|
617
638
|
type: "button",
|
|
618
639
|
tabIndex: -1,
|
|
619
640
|
...pressable,
|
|
620
|
-
disabled: isDisabled || isReadOnly || !canDecrement,
|
|
641
|
+
"aria-disabled": isDisabled || isReadOnly || !canDecrement || undefined,
|
|
621
642
|
"aria-label": t('@astryx.numberInput.decrementLabel', {
|
|
622
643
|
label
|
|
623
644
|
}),
|
|
624
645
|
onPointerDown: event => event.preventDefault(),
|
|
625
646
|
onClick: () => {
|
|
647
|
+
// An aria-disabled stepper does nothing, as a natively disabled
|
|
648
|
+
// one did (spec:AST-085 FR2).
|
|
649
|
+
if (isDisabled || isReadOnly || !canDecrement) {
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
626
652
|
inputRef.current?.focus();
|
|
627
653
|
stepValue(-1);
|
|
628
654
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SideNavHeading.d.ts","sourceRoot":"","sources":["../../src/SideNav/SideNavHeading.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAkB,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAsBtD,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,eAAe,CAAC;AAGrD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"SideNavHeading.d.ts","sourceRoot":"","sources":["../../src/SideNav/SideNavHeading.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAkB,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAsBtD,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,eAAe,CAAC;AAGrD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAsO5C,MAAM,WAAW,mBAAoB,SAAQ,SAAS,CAAC,cAAc,CAAC;IACpE,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;;OAIG;IACH,EAAE,CAAC,EAAE,iBAAiB,CAAC;IACvB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,SAAS,CAAC;IAC7B;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AAQD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,cAAc,CAAC,EAC7B,EAAE,EACF,IAAI,EACJ,OAAO,EACP,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,IAAI,EACJ,MAAM,EACN,SAAS,EACT,KAAK,EACL,aAAa,EAAE,MAAM,EACrB,GAAG,EACH,GAAG,KAAK,EACT,EAAE,mBAAmB,sCAgiBrB;yBAjjBe,cAAc"}
|
|
@@ -129,6 +129,17 @@ const styles = {
|
|
|
129
129
|
kGuDYH: "x1qlqyl8",
|
|
130
130
|
$$css: true
|
|
131
131
|
},
|
|
132
|
+
linkBesideEndContent: {
|
|
133
|
+
k1xSpc: "x78zum5",
|
|
134
|
+
kGNEyG: "x6s0dn4",
|
|
135
|
+
kSGwAc: "xkh2ocl",
|
|
136
|
+
kOIVth: "x1txdalj",
|
|
137
|
+
kzQI83: "x1iyjqo2",
|
|
138
|
+
k7Eaqz: "xeuugli",
|
|
139
|
+
kybGjl: "x1hl2dhg",
|
|
140
|
+
kMwMTN: "x1heor9g",
|
|
141
|
+
$$css: true
|
|
142
|
+
},
|
|
132
143
|
popoverHeading: {
|
|
133
144
|
k1xSpc: "x78zum5",
|
|
134
145
|
kGNEyG: "x6s0dn4",
|
|
@@ -484,6 +495,29 @@ export function SideNavHeading({
|
|
|
484
495
|
}))]
|
|
485
496
|
});
|
|
486
497
|
|
|
498
|
+
// Whole heading is a link (no menu, single headingHref). End content may
|
|
499
|
+
// be a control, which must not sit inside the link (no interactive content
|
|
500
|
+
// in an <a>), so with end content the row is a div holding the link and the
|
|
501
|
+
// end content side by side, as the mixed mode does.
|
|
502
|
+
if (isWholeHeadingLink && headingHref && headerEndContentElement) {
|
|
503
|
+
return /*#__PURE__*/_jsxs("div", {
|
|
504
|
+
ref: ref,
|
|
505
|
+
"data-testid": testId,
|
|
506
|
+
...mergeProps(themeProps('side-nav-heading'), stylex.props(styles.root, xstyle), className, style),
|
|
507
|
+
children: [/*#__PURE__*/_jsxs(LinkComponent, {
|
|
508
|
+
href: headingHref,
|
|
509
|
+
...focusOutlineProps.focusVisible(styles.linkBesideEndContent, styles.menuTrigger),
|
|
510
|
+
...props,
|
|
511
|
+
children: [icon && /*#__PURE__*/_jsx("span", {
|
|
512
|
+
...{
|
|
513
|
+
className: "x2lah0s x78zum5 x6s0dn4 xl56j7k"
|
|
514
|
+
},
|
|
515
|
+
children: icon
|
|
516
|
+
}), renderTextContent()]
|
|
517
|
+
}), headerEndContentElement]
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
|
|
487
521
|
// Whole heading is a link (no menu, single headingHref)
|
|
488
522
|
if (isWholeHeadingLink && headingHref) {
|
|
489
523
|
return /*#__PURE__*/_jsxs(LinkComponent, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Skeleton.d.ts","sourceRoot":"","sources":["../../src/Skeleton/Skeleton.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,MAAM,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"Skeleton.d.ts","sourceRoot":"","sources":["../../src/Skeleton/Skeleton.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,MAAM,MAAM,kBAAkB,CAAC;AAgG3C,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;EAuBhB,CAAC;AAgBH,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,YAAY,CAAC;AAMvD,MAAM,WAAW,aAAc,SAAQ,SAAS,CAAC,cAAc,CAAC;IAC9D,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB;;;;;;;;;;OAUG;IACH,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,QAAQ,CAAC,EACvB,KAAc,EACd,MAAe,EACf,MAAM,EAAE,UAAc,EACtB,KAAS,EACT,aAAa,EAAE,MAAM,EACrB,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,KAAK,EACT,EAAE,aAAa,+BA0Bf;yBArCe,QAAQ"}
|