@astryxdesign/core 0.7.3-canary.296edaf → 0.7.3-canary.2f47a4a
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/DropdownMenu/DropdownMenuRadioGroup.d.ts +9 -5
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuRadioGroup.js +2 -0
- package/dist/TimeInput/TimeInput.d.ts +5 -5
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +37 -20
- package/dist/TimeInput/TouchTimeField.d.ts.map +1 -1
- package/dist/TimeInput/TouchTimeField.js +17 -6
- package/package.json +3 -3
- package/src/DropdownMenu/DropdownMenuItem.doc.mjs +66 -7
- package/src/DropdownMenu/DropdownMenuItem.spec.md +209 -0
- package/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs +62 -7
- package/src/DropdownMenu/DropdownMenuRadioGroup.spec.md +190 -0
- package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +10 -4
- package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +93 -0
- package/src/DropdownMenu/__tests__/DropdownMenuItemAudit.a11y.chromium.spec.ts +366 -0
- package/src/DropdownMenu/__tests__/DropdownMenuRadioGroupAudit.a11y.chromium.spec.ts +327 -0
- package/src/TimeInput/NativeTimeInput.test.tsx +22 -0
- package/src/TimeInput/Presentation.test.tsx +23 -0
- package/src/TimeInput/TimeInput.doc.mjs +8 -7
- package/src/TimeInput/TimeInput.test.tsx +119 -7
- package/src/TimeInput/TimeInput.tsx +51 -23
- package/src/TimeInput/TouchTimeField.tsx +27 -5
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* comes from the parent DropdownMenu's useListFocus, which matches
|
|
10
10
|
* menuitemradio rows.
|
|
11
11
|
*/
|
|
12
|
-
import { type ReactNode } from 'react';
|
|
12
|
+
import { type ReactNode, type Ref } from 'react';
|
|
13
13
|
import type { BaseProps } from '../BaseProps';
|
|
14
14
|
export interface DropdownMenuRadioGroupProps extends Omit<BaseProps, 'onChange'> {
|
|
15
15
|
/**
|
|
@@ -24,9 +24,9 @@ export interface DropdownMenuRadioGroupProps extends Omit<BaseProps, 'onChange'>
|
|
|
24
24
|
/**
|
|
25
25
|
* Accessible name for the group, announced by screen readers so the radios
|
|
26
26
|
* read as a named set (e.g. "Sort by"). Applied as the group's `aria-label`.
|
|
27
|
-
* Required
|
|
28
|
-
*
|
|
29
|
-
*
|
|
27
|
+
* Required even when `aria-labelledby` (via base props) points to visible
|
|
28
|
+
* text; that visible reference becomes the computed name while `label`
|
|
29
|
+
* remains the fallback.
|
|
30
30
|
*/
|
|
31
31
|
label: string;
|
|
32
32
|
/**
|
|
@@ -36,6 +36,10 @@ export interface DropdownMenuRadioGroupProps extends Omit<BaseProps, 'onChange'>
|
|
|
36
36
|
* @default true
|
|
37
37
|
*/
|
|
38
38
|
hasCloseOnSelect?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Ref forwarded to the role="group" root.
|
|
41
|
+
*/
|
|
42
|
+
ref?: Ref<HTMLDivElement>;
|
|
39
43
|
/**
|
|
40
44
|
* The `DropdownMenuRadioItem`s that make up the group.
|
|
41
45
|
*/
|
|
@@ -58,7 +62,7 @@ export interface DropdownMenuRadioGroupProps extends Omit<BaseProps, 'onChange'>
|
|
|
58
62
|
* </DropdownMenu>
|
|
59
63
|
* ```
|
|
60
64
|
*/
|
|
61
|
-
export declare function DropdownMenuRadioGroup({ value, onChange, label, hasCloseOnSelect, children, xstyle, className, style, ...rest }: DropdownMenuRadioGroupProps): import("react").JSX.Element;
|
|
65
|
+
export declare function DropdownMenuRadioGroup({ value, onChange, label, hasCloseOnSelect, children, ref, xstyle, className, style, ...rest }: DropdownMenuRadioGroupProps): import("react").JSX.Element;
|
|
62
66
|
export declare namespace DropdownMenuRadioGroup {
|
|
63
67
|
var displayName: string;
|
|
64
68
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DropdownMenuRadioGroup.d.ts","sourceRoot":"","sources":["../../src/DropdownMenu/DropdownMenuRadioGroup.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,OAAO,EAAU,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"DropdownMenuRadioGroup.d.ts","sourceRoot":"","sources":["../../src/DropdownMenu/DropdownMenuRadioGroup.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,OAAO,EAAU,KAAK,SAAS,EAAE,KAAK,GAAG,EAAC,MAAM,OAAO,CAAC;AAIxD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAiB5C,MAAM,WAAW,2BAA4B,SAAQ,IAAI,CACvD,SAAS,EACT,UAAU,CACX;IACC;;;OAGG;IACH,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B;;OAEG;IACH,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC;;;;;;OAMG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;OAEG;IACH,GAAG,CAAC,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;IAC1B;;OAEG;IACH,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,KAAK,EACL,QAAQ,EACR,KAAK,EACL,gBAAuB,EACvB,QAAQ,EACR,GAAG,EACH,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,IAAI,EACR,EAAE,2BAA2B,+BAkB7B;yBA7Be,sBAAsB"}
|
|
@@ -51,6 +51,7 @@ export function DropdownMenuRadioGroup({
|
|
|
51
51
|
label,
|
|
52
52
|
hasCloseOnSelect = true,
|
|
53
53
|
children,
|
|
54
|
+
ref,
|
|
54
55
|
xstyle,
|
|
55
56
|
className,
|
|
56
57
|
style,
|
|
@@ -63,6 +64,7 @@ export function DropdownMenuRadioGroup({
|
|
|
63
64
|
}), [value, onChange, hasCloseOnSelect]);
|
|
64
65
|
return /*#__PURE__*/_jsx("div", {
|
|
65
66
|
...rest,
|
|
67
|
+
ref: ref,
|
|
66
68
|
role: "group",
|
|
67
69
|
"aria-label": label,
|
|
68
70
|
...mergeProps(stylex.props(styles.group, xstyle), {
|
|
@@ -59,12 +59,12 @@ export interface TimeInputProps extends Omit<FieldInputBaseProps<HTMLInputElemen
|
|
|
59
59
|
/**
|
|
60
60
|
* Explains why the input is disabled. When set together with
|
|
61
61
|
* `isDisabled`, the input shows a tooltip with this text on hover and
|
|
62
|
-
* keyboard focus, and the
|
|
63
|
-
*
|
|
64
|
-
*
|
|
62
|
+
* keyboard focus, and the input is described by it, so the reason is
|
|
63
|
+
* discoverable by keyboard and assistive technology. Typing and arrow-key
|
|
64
|
+
* adjustment stay blocked.
|
|
65
65
|
*
|
|
66
|
-
* Use this instead of wrapping a disabled input in `Tooltip
|
|
67
|
-
*
|
|
66
|
+
* Use this instead of wrapping a disabled input in `Tooltip`, so the
|
|
67
|
+
* reason is tied to the field.
|
|
68
68
|
*
|
|
69
69
|
* @example
|
|
70
70
|
* ```
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimeInput.d.ts","sourceRoot":"","sources":["../../src/TimeInput/TimeInput.tsx"],"names":[],"mappings":"AA+BA,OAAO,KAAK,MAAM,MAAM,kBAAkB,CAAC;AAO3C,OAAO,EAGL,KAAK,WAAW,EAKhB,KAAK,kBAAkB,EACxB,MAAM,UAAU,CAAC;AAIlB,OAAO,EACL,KAAK,aAAa,EAUnB,MAAM,UAAU,CAAC;AAGlB,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,gBAAgB,CAAC;AAgB9C,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"TimeInput.d.ts","sourceRoot":"","sources":["../../src/TimeInput/TimeInput.tsx"],"names":[],"mappings":"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"}
|
|
@@ -46,6 +46,13 @@ import { effectiveInputPresentation, resolveInputPresentation } from "../utils/i
|
|
|
46
46
|
import { useDevWarning } from "../hooks/useDevWarning.js";
|
|
47
47
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
48
48
|
const TOUCH_POINTER_QUERY = '(pointer: coarse)';
|
|
49
|
+
const styles = {
|
|
50
|
+
disabledFocusRing: {
|
|
51
|
+
kVAM5u: "xvy26l8 x6q1khz",
|
|
52
|
+
kGVxlE: "x1gnnqk1 xuihb5h",
|
|
53
|
+
$$css: true
|
|
54
|
+
}
|
|
55
|
+
};
|
|
49
56
|
const sizeStyles = {
|
|
50
57
|
sm: {
|
|
51
58
|
kZKoxP: "x6k0iem",
|
|
@@ -175,10 +182,10 @@ function TimeField({
|
|
|
175
182
|
}
|
|
176
183
|
}, [announce, inputGroup, status?.message, status?.type]);
|
|
177
184
|
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
//
|
|
181
|
-
//
|
|
185
|
+
// A disabled field never renders native `disabled` (spec:AST-085 FR1): the
|
|
186
|
+
// input stays focusable via aria-disabled, and readOnly plus the value
|
|
187
|
+
// mutation guards block typing. The disabled-reason tooltip's listeners
|
|
188
|
+
// attach to the input container.
|
|
182
189
|
const showsDisabledMessage = isDisabled && !!disabledMessage;
|
|
183
190
|
const disabledMessageTooltip = useTooltip({
|
|
184
191
|
placement: 'above',
|
|
@@ -258,8 +265,8 @@ function TimeField({
|
|
|
258
265
|
|
|
259
266
|
// Handle input text change - update immediately if valid
|
|
260
267
|
const handleInputChange = useCallback(e => {
|
|
261
|
-
//
|
|
262
|
-
//
|
|
268
|
+
// The input is never natively disabled (spec:AST-085 FR1), so guard
|
|
269
|
+
// value mutation explicitly (readOnly also blocks typing).
|
|
263
270
|
if (isDisabled) {
|
|
264
271
|
return;
|
|
265
272
|
}
|
|
@@ -290,6 +297,11 @@ function TimeField({
|
|
|
290
297
|
if (pendingInput === null) {
|
|
291
298
|
return;
|
|
292
299
|
}
|
|
300
|
+
// Disabled: keep the value, drop the draft (spec:AST-085 FR2).
|
|
301
|
+
if (isDisabled) {
|
|
302
|
+
setPendingInput(null);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
293
305
|
if (!pendingInput.trim()) {
|
|
294
306
|
// Empty input clears the value
|
|
295
307
|
if (value !== undefined) {
|
|
@@ -307,9 +319,15 @@ function TimeField({
|
|
|
307
319
|
}
|
|
308
320
|
// Clear pending input - display will revert to formatted value
|
|
309
321
|
setPendingInput(null);
|
|
310
|
-
}, [pendingInput, value, fireChange, hasSeconds, min, max]);
|
|
322
|
+
}, [pendingInput, isDisabled, value, fireChange, hasSeconds, min, max]);
|
|
311
323
|
|
|
312
324
|
// Handle keyboard navigation on input
|
|
325
|
+
// Enter in a focused disabled field would submit a surrounding form (FR2).
|
|
326
|
+
const preventDisabledEnter = e => {
|
|
327
|
+
if (isDisabled && e.key === 'Enter') {
|
|
328
|
+
e.preventDefault();
|
|
329
|
+
}
|
|
330
|
+
};
|
|
313
331
|
const handleInputKeyDown = useCallback(e => {
|
|
314
332
|
// ArrowUp/ArrowDown step the time and preventDefault; an IME candidate
|
|
315
333
|
// window uses those same arrows to navigate candidates, so guard the
|
|
@@ -318,8 +336,7 @@ function TimeField({
|
|
|
318
336
|
if (isImeKeyEvent(e.nativeEvent)) {
|
|
319
337
|
return;
|
|
320
338
|
}
|
|
321
|
-
// Arrow-key adjustment mutates the value; block it while
|
|
322
|
-
// disabled reason (the input keeps focusability via aria-disabled).
|
|
339
|
+
// Arrow-key adjustment mutates the value; block it while disabled.
|
|
323
340
|
if (isDisabled) {
|
|
324
341
|
return;
|
|
325
342
|
}
|
|
@@ -393,7 +410,7 @@ function TimeField({
|
|
|
393
410
|
size,
|
|
394
411
|
status: status?.type ?? null,
|
|
395
412
|
disabled: isDisabled ? 'disabled' : null
|
|
396
|
-
}), stylex.props(inputWrapperStyles.base, sizeStyles[size], isDisabled && inputWrapperStyles.disabled, status && inputStatusBorderStyles[status.type], status && !isDisabled && inputStatusHoverShadowStyles[status.type], status && inputStatusFocusWithinStyles[status.type], inputGroup && groupStyles.inGroup, controlXstyle), controlClassName, controlStyle),
|
|
413
|
+
}), stylex.props(inputWrapperStyles.base, sizeStyles[size], isDisabled && inputWrapperStyles.disabled, isDisabled && styles.disabledFocusRing, status && inputStatusBorderStyles[status.type], status && !isDisabled && inputStatusHoverShadowStyles[status.type], status && inputStatusFocusWithinStyles[status.type], inputGroup && groupStyles.inGroup, controlXstyle), controlClassName, controlStyle),
|
|
397
414
|
children: [inputGroup && /*#__PURE__*/_jsx(VisuallyHidden, {
|
|
398
415
|
id: inputLabelID,
|
|
399
416
|
children: label
|
|
@@ -420,7 +437,7 @@ function TimeField({
|
|
|
420
437
|
max: max,
|
|
421
438
|
hourFormat: hourFormat,
|
|
422
439
|
isEffectivelyDisabled: isDisabled,
|
|
423
|
-
isFocusableDisabled:
|
|
440
|
+
isFocusableDisabled: true,
|
|
424
441
|
isEffectivelyRequired: isEffectivelyRequired,
|
|
425
442
|
isBusy: isBusy,
|
|
426
443
|
statusType: status?.type,
|
|
@@ -429,7 +446,8 @@ function TimeField({
|
|
|
429
446
|
...controlProps,
|
|
430
447
|
onFocus: onFocusProp,
|
|
431
448
|
onBlur: onBlurProp,
|
|
432
|
-
onKeyDown: onKeyDownProp
|
|
449
|
+
onKeyDown: chainEventHandlers(preventDisabledEnter, onKeyDownProp),
|
|
450
|
+
onClick: isDisabled ? undefined : controlProps.onClick
|
|
433
451
|
}
|
|
434
452
|
}) : /*#__PURE__*/_jsxs(_Fragment, {
|
|
435
453
|
children: [/*#__PURE__*/_jsx("div", {
|
|
@@ -450,15 +468,14 @@ function TimeField({
|
|
|
450
468
|
onChange: handleInputChange,
|
|
451
469
|
onFocus: chainEventHandlers(handleFocus, onFocusProp),
|
|
452
470
|
onBlur: chainEventHandlers(handleBlur, onBlurProp),
|
|
453
|
-
onKeyDown: chainEventHandlers(handleInputKeyDown, onKeyDownProp),
|
|
454
|
-
placeholder: displayPlaceholder
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
//
|
|
471
|
+
onKeyDown: chainEventHandlers(preventDisabledEnter, handleInputKeyDown, onKeyDownProp),
|
|
472
|
+
placeholder: displayPlaceholder,
|
|
473
|
+
"aria-disabled": isDisabled ? 'true' : undefined,
|
|
474
|
+
readOnly: isDisabled || undefined
|
|
475
|
+
// The browser still dispatches a click on an aria-disabled input,
|
|
476
|
+
// so a caller's onClick is skipped too (spec:AST-085 FR2).
|
|
458
477
|
,
|
|
459
|
-
|
|
460
|
-
"aria-disabled": showsDisabledMessage ? 'true' : undefined,
|
|
461
|
-
readOnly: showsDisabledMessage || undefined,
|
|
478
|
+
onClick: isDisabled ? undefined : controlProps.onClick,
|
|
462
479
|
autoFocus: hasAutoFocus,
|
|
463
480
|
"data-autofocus": hasAutoFocus || undefined,
|
|
464
481
|
"aria-describedby": ariaDescribedBy,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TouchTimeField.d.ts","sourceRoot":"","sources":["../../src/TimeInput/TouchTimeField.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"TouchTimeField.d.ts","sourceRoot":"","sources":["../../src/TimeInput/TouchTimeField.tsx"],"names":[],"mappings":"AAoEA,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,aAAa,CAAC;AAqHhD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,aAAqB,EACrB,WAAW,EACX,UAAkB,EAClB,UAAkB,EAClB,UAAkB,EAClB,eAAe,EACf,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,SAAiB,EACjB,GAAG,EACH,GAAG,EACH,UAAkB,EAClB,QAAgB,EAChB,UAAkB,EAGlB,SAAS,EAAE,UAAU,EACrB,YAAY,EAAE,aAAa,EAC3B,WAAW,EAAE,oBAAoB,EACjC,IAAI,EAAE,QAAQ,EACd,MAAM,EACN,aAA0B,EAC1B,YAAY,EACZ,KAAK,EACL,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,GAAG,EACH,GAAG,SAAS,EACb,EAAE,cAAc,+BAiZhB;yBApbe,cAAc"}
|
|
@@ -30,7 +30,7 @@ import { useResolvedRequired } from "../hooks/useResolvedRequired.js";
|
|
|
30
30
|
import { Icon } from "../Icon/index.js";
|
|
31
31
|
import { useTranslator } from "../i18n/index.js";
|
|
32
32
|
import "../theme/tokens.stylex.js";
|
|
33
|
-
import { sizeVars, spacingVars } from "../theme/tokens.stylex.js";
|
|
33
|
+
import { colorVars, sizeVars, spacingVars } from "../theme/tokens.stylex.js";
|
|
34
34
|
import { useSize } from "../SizeContext/SizeContext.js";
|
|
35
35
|
import { Spinner } from "../Spinner/index.js";
|
|
36
36
|
import { useTooltip } from "../Tooltip/index.js";
|
|
@@ -60,6 +60,11 @@ const sizeStyles = {
|
|
|
60
60
|
}
|
|
61
61
|
};
|
|
62
62
|
const styles = {
|
|
63
|
+
disabledFocusRing: {
|
|
64
|
+
kVAM5u: "xvy26l8 x6q1khz",
|
|
65
|
+
kGVxlE: "x1gnnqk1 xuihb5h",
|
|
66
|
+
$$css: true
|
|
67
|
+
},
|
|
63
68
|
iconButton: {
|
|
64
69
|
k1xSpc: "x78zum5",
|
|
65
70
|
kGNEyG: "x6s0dn4",
|
|
@@ -309,11 +314,11 @@ export function TouchTimeField({
|
|
|
309
314
|
size,
|
|
310
315
|
status: status?.type ?? null,
|
|
311
316
|
disabled: isDisabled ? 'disabled' : null
|
|
312
|
-
}), stylex.props(inputWrapperStyles.base, sizeStyles[size], isDisabled && inputWrapperStyles.disabled, status && inputStatusBorderStyles[status.type], status && !isDisabled && inputStatusHoverShadowStyles[status.type], status && inputStatusFocusWithinStyles[status.type], inputGroup && groupStyles.inGroup, controlXstyle), controlClassName, controlStyle),
|
|
317
|
+
}), stylex.props(inputWrapperStyles.base, sizeStyles[size], isDisabled && inputWrapperStyles.disabled, isDisabled && styles.disabledFocusRing, status && inputStatusBorderStyles[status.type], status && !isDisabled && inputStatusHoverShadowStyles[status.type], status && inputStatusFocusWithinStyles[status.type], inputGroup && groupStyles.inGroup, controlXstyle), controlClassName, controlStyle),
|
|
313
318
|
children: [/*#__PURE__*/_jsx("button", {
|
|
314
319
|
type: "button",
|
|
315
320
|
onClick: openSheet,
|
|
316
|
-
disabled: isEffectivelyDisabled,
|
|
321
|
+
"aria-disabled": isEffectivelyDisabled || undefined,
|
|
317
322
|
"aria-label": t('@astryx.timeInput.openPicker', {
|
|
318
323
|
label
|
|
319
324
|
}),
|
|
@@ -334,10 +339,16 @@ export function TouchTimeField({
|
|
|
334
339
|
readOnly: true,
|
|
335
340
|
inputMode: "none",
|
|
336
341
|
onChange: () => {},
|
|
337
|
-
onClick: chainEventHandlers(openSheet, onClickProp)
|
|
342
|
+
onClick: isEffectivelyDisabled ? undefined : chainEventHandlers(openSheet, onClickProp)
|
|
343
|
+
// Enter would submit a surrounding form while disabled (FR2).
|
|
344
|
+
,
|
|
345
|
+
onKeyDown: chainEventHandlers(e => {
|
|
346
|
+
if (isEffectivelyDisabled && e.key === 'Enter') {
|
|
347
|
+
e.preventDefault();
|
|
348
|
+
}
|
|
349
|
+
}, controlProps.onKeyDown),
|
|
338
350
|
placeholder: placeholder,
|
|
339
|
-
disabled: isEffectivelyDisabled
|
|
340
|
-
"aria-disabled": showsDisabledMessage ? 'true' : undefined,
|
|
351
|
+
"aria-disabled": isEffectivelyDisabled ? 'true' : undefined,
|
|
341
352
|
"aria-describedby": ariaDescribedBy,
|
|
342
353
|
"aria-required": isEffectivelyRequired ? 'true' : undefined,
|
|
343
354
|
"aria-invalid": status?.type === 'error' ? 'true' : undefined,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astryxdesign/core",
|
|
3
|
-
"version": "0.7.3-canary.
|
|
3
|
+
"version": "0.7.3-canary.2f47a4a",
|
|
4
4
|
"displayName": "Astryx Core",
|
|
5
5
|
"description": "The component library. Accessible, themeable React components with built-in spacing, dark mode, and StyleX styling.",
|
|
6
6
|
"author": "Meta Open Source",
|
|
@@ -792,8 +792,8 @@
|
|
|
792
792
|
"react-dom": ">=19.0.0"
|
|
793
793
|
},
|
|
794
794
|
"devDependencies": {
|
|
795
|
-
"@astryxdesign/a11y-spec": "0.7.3-canary.
|
|
796
|
-
"@astryxdesign/cli": "0.7.3-canary.
|
|
795
|
+
"@astryxdesign/a11y-spec": "0.7.3-canary.2f47a4a",
|
|
796
|
+
"@astryxdesign/cli": "0.7.3-canary.2f47a4a",
|
|
797
797
|
"@babel/cli": "^7.29.7",
|
|
798
798
|
"@babel/core": "^7.29.7",
|
|
799
799
|
"@babel/preset-react": "^7.29.7",
|
|
@@ -8,7 +8,50 @@ export const docs = {
|
|
|
8
8
|
displayName: 'Dropdown Menu Item',
|
|
9
9
|
isHiddenFromOverview: true,
|
|
10
10
|
description:
|
|
11
|
-
'
|
|
11
|
+
'One action or destination row in the pointer presentation of DropdownMenu. The parent menu owns keyboard navigation; the row owns activation, disabled state, destination behavior, content layout, and its theme target. Supported host attributes, DOM handlers, className, style, xstyle, and ref reach or compose on the role="menuitem" root.',
|
|
12
|
+
usage: {
|
|
13
|
+
description:
|
|
14
|
+
'Use DropdownMenuItem inside DropdownMenu compound mode for one action or destination. Keep label, description, icon, and endContent non-interactive; controls belong in separate rows or the menu header/footer.',
|
|
15
|
+
bestPractices: [
|
|
16
|
+
{
|
|
17
|
+
guidance: true,
|
|
18
|
+
description:
|
|
19
|
+
'Use href for a destination so LinkProvider, modified clicks, middle clicks, and browser link affordances remain available.',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
guidance: true,
|
|
23
|
+
description:
|
|
24
|
+
'Use hasCloseOnSelect=false only when activation reports a result in place on the same row.',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
guidance: false,
|
|
28
|
+
description:
|
|
29
|
+
'Put buttons, links, or other focusable controls inside label, description, or endContent. Each menu row is one interactive target.',
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
anatomy: [
|
|
33
|
+
{
|
|
34
|
+
name: 'Row',
|
|
35
|
+
required: true,
|
|
36
|
+
description:
|
|
37
|
+
'The role="menuitem" root that owns activation, disabled state, destination behavior, and theme state.',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: 'Content',
|
|
41
|
+
required: true,
|
|
42
|
+
description:
|
|
43
|
+
'The non-interactive label plus optional icon, description, and end content.',
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
theming: {
|
|
48
|
+
targets: [
|
|
49
|
+
{
|
|
50
|
+
className: 'astryx-dropdown-menu-item',
|
|
51
|
+
visualProps: ['size', 'variant'],
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
},
|
|
12
55
|
playground: {
|
|
13
56
|
// Standalone DropdownMenuItem has no required props, so the properties-tab
|
|
14
57
|
// preview renders an empty row without seeded content. Seed a label and
|
|
@@ -26,6 +69,7 @@ export const docs = {
|
|
|
26
69
|
name: 'label',
|
|
27
70
|
type: 'ReactNode',
|
|
28
71
|
description: 'Primary label text.',
|
|
72
|
+
required: true,
|
|
29
73
|
},
|
|
30
74
|
{
|
|
31
75
|
name: 'description',
|
|
@@ -55,11 +99,18 @@ export const docs = {
|
|
|
55
99
|
description:
|
|
56
100
|
'Link relationship. noopener noreferrer is added for target="_blank". Only used with href.',
|
|
57
101
|
},
|
|
102
|
+
{
|
|
103
|
+
name: 'isDisabled',
|
|
104
|
+
type: 'boolean',
|
|
105
|
+
description:
|
|
106
|
+
'Whether the row is unavailable. Disabled rows expose aria-disabled, remove link destinations, and suppress activation.',
|
|
107
|
+
default: 'false',
|
|
108
|
+
},
|
|
58
109
|
{
|
|
59
110
|
name: 'endContent',
|
|
60
111
|
type: 'ReactNode',
|
|
61
112
|
description:
|
|
62
|
-
'
|
|
113
|
+
'Non-interactive content rendered after the label and description.',
|
|
63
114
|
},
|
|
64
115
|
{
|
|
65
116
|
name: 'hasCloseOnSelect',
|
|
@@ -95,7 +146,7 @@ export const docsZh = {
|
|
|
95
146
|
isHiddenFromOverview: true,
|
|
96
147
|
displayName: 'Dropdown Menu Item',
|
|
97
148
|
description:
|
|
98
|
-
'
|
|
149
|
+
'DropdownMenu 指针呈现中的一个操作或目标行。父菜单负责键盘导航;该行负责激活、禁用状态、目标行为、内容布局和主题目标。支持的宿主属性、DOM 处理程序、className、style、xstyle 和 ref 会落在或组合到 role="menuitem" 根元素上。',
|
|
99
150
|
props: [
|
|
100
151
|
{
|
|
101
152
|
name: 'icon',
|
|
@@ -106,6 +157,7 @@ export const docsZh = {
|
|
|
106
157
|
name: 'label',
|
|
107
158
|
type: 'ReactNode',
|
|
108
159
|
description: '主标签文本。',
|
|
160
|
+
required: true,
|
|
109
161
|
},
|
|
110
162
|
{
|
|
111
163
|
name: 'description',
|
|
@@ -134,10 +186,16 @@ export const docsZh = {
|
|
|
134
186
|
type: 'string',
|
|
135
187
|
description: '链接关系。target="_blank" 时自动加入 noopener noreferrer。',
|
|
136
188
|
},
|
|
189
|
+
{
|
|
190
|
+
name: 'isDisabled',
|
|
191
|
+
type: 'boolean',
|
|
192
|
+
description: '该行是否不可用;不可用的行不会激活或导航。',
|
|
193
|
+
default: 'false',
|
|
194
|
+
},
|
|
137
195
|
{
|
|
138
196
|
name: 'endContent',
|
|
139
197
|
type: 'ReactNode',
|
|
140
|
-
description: '
|
|
198
|
+
description: '在标签和描述之后渲染的非交互式附加内容。',
|
|
141
199
|
},
|
|
142
200
|
{
|
|
143
201
|
name: 'hasCloseOnSelect',
|
|
@@ -168,9 +226,9 @@ export const docsZh = {
|
|
|
168
226
|
|
|
169
227
|
export const docsDense = {
|
|
170
228
|
name: 'DropdownMenuItem',
|
|
171
|
-
isHiddenFromOverview: true,
|
|
172
229
|
displayName: 'Dropdown Menu Item',
|
|
173
|
-
description:
|
|
230
|
+
description:
|
|
231
|
+
'one pointer-menu action/destination row; parent owns keyboard, row owns activation, state, destination, content, and root inputs',
|
|
174
232
|
propDescriptions: {
|
|
175
233
|
icon: 'icon before label',
|
|
176
234
|
label: 'primary label text',
|
|
@@ -180,7 +238,8 @@ export const docsDense = {
|
|
|
180
238
|
href: 'row is a real anchor w/ role=menuitem; modified/middle click keeps browser meaning',
|
|
181
239
|
target: "link target ('_blank' | '_self'), with href",
|
|
182
240
|
rel: 'link rel; noopener noreferrer added for _blank',
|
|
183
|
-
|
|
241
|
+
isDisabled: 'unavailable; removes destinations and suppresses activation',
|
|
242
|
+
endContent: 'non-interactive content after label+description',
|
|
184
243
|
hasCloseOnSelect:
|
|
185
244
|
'false keeps the menu open on activation (in-place result on the item)',
|
|
186
245
|
variant:
|