@hjmds/react-native 1.28.0 → 1.28.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/auth-screen.d.ts +13 -2
  2. package/dist/auth-screen.d.ts.map +1 -1
  3. package/dist/auth-screen.js +21 -7
  4. package/dist/auth-screen.js.map +1 -1
  5. package/dist/collection-rail.d.ts +7 -1
  6. package/dist/collection-rail.d.ts.map +1 -1
  7. package/dist/collection-rail.js +12 -5
  8. package/dist/collection-rail.js.map +1 -1
  9. package/dist/evidence.d.ts +1 -1
  10. package/dist/evidence.js +1 -1
  11. package/dist/feedback.d.ts +7 -1
  12. package/dist/feedback.d.ts.map +1 -1
  13. package/dist/feedback.js +4 -2
  14. package/dist/feedback.js.map +1 -1
  15. package/dist/inputs.d.ts +6 -0
  16. package/dist/inputs.d.ts.map +1 -1
  17. package/dist/inputs.js +4 -1
  18. package/dist/inputs.js.map +1 -1
  19. package/dist/internal/initial-focus.d.ts +11 -0
  20. package/dist/internal/initial-focus.d.ts.map +1 -0
  21. package/dist/internal/initial-focus.js +60 -0
  22. package/dist/internal/initial-focus.js.map +1 -0
  23. package/dist/internal/list-row-scroll-room.d.ts +2 -1
  24. package/dist/internal/list-row-scroll-room.d.ts.map +1 -1
  25. package/dist/internal/list-row-scroll-room.js +2 -1
  26. package/dist/internal/list-row-scroll-room.js.map +1 -1
  27. package/dist/internal/screen-body-inset.d.ts +9 -0
  28. package/dist/internal/screen-body-inset.d.ts.map +1 -0
  29. package/dist/internal/screen-body-inset.js +10 -0
  30. package/dist/internal/screen-body-inset.js.map +1 -0
  31. package/dist/keyboard.d.ts +1 -1
  32. package/dist/keyboard.d.ts.map +1 -1
  33. package/dist/keyboard.js.map +1 -1
  34. package/dist/overlays.d.ts +10 -2
  35. package/dist/overlays.d.ts.map +1 -1
  36. package/dist/overlays.js +228 -206
  37. package/dist/overlays.js.map +1 -1
  38. package/dist/screen-flows.d.ts +9 -4
  39. package/dist/screen-flows.d.ts.map +1 -1
  40. package/dist/screen-flows.js +12 -9
  41. package/dist/screen-flows.js.map +1 -1
  42. package/dist/screens.d.ts +23 -2
  43. package/dist/screens.d.ts.map +1 -1
  44. package/dist/screens.js +21 -6
  45. package/dist/screens.js.map +1 -1
  46. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"overlays.d.ts","sourceRoot":"","sources":["../src/overlays.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"overlays.d.ts","sourceRoot":"","sources":["../src/overlays.tsx"],"names":[],"mappings":"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"}
package/dist/overlays.js CHANGED
@@ -1,4 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ScreenBodyInsetContext } from "./internal/screen-body-inset.js";
3
+ import { InitialFocusScope } from "./internal/initial-focus.js";
4
+ import { initialFocusDefaults } from "@hjmds/design-contracts/screen-patterns";
2
5
  import { resolveOriginTransition } from "@hjmds/design-contracts/content-transition";
3
6
  import { focusAccessibleHost } from "./internal/accessible-focus.js";
4
7
  import { createAlertDialogSession, getAlertDialogInitialFocus, validateAlertDialogRequest, } from "@hjmds/design-contracts/components/alert-dialog";
@@ -149,7 +152,7 @@ function resolveOverlayAccessibleTitle(title, accessibilityTitle) {
149
152
  throw new TypeError("An element `title` requires `accessibilityTitle` for the modal accessible name");
150
153
  }
151
154
  /** Native modal boundary with one reasoned close intent for each user attempt. */
152
- export function Dialog({ open, defaultOpen, onOpenChange, title, accessibilityTitle, description, children, primaryAction, secondaryAction, dismissible = true, busy: externalBusy = false, onActionError, size = dialogRecipe.defaults.size, motionOrigin, closeLabel, returnFocusRef, contentStyle, onShow, ...modalProps }) {
155
+ export function Dialog({ open, defaultOpen, onOpenChange, title, accessibilityTitle, description, children, primaryAction, secondaryAction, dismissible = true, busy: externalBusy = false, onActionError, size = dialogRecipe.defaults.size, motionOrigin, closeLabel, returnFocusRef, contentStyle, onShow, initialFocus = initialFocusDefaults.overlay, ...modalProps }) {
153
156
  const [actionPending, setActionPending] = useState(null);
154
157
  const actionRun = useRef(null);
155
158
  const busy = externalBusy || actionPending !== null;
@@ -177,6 +180,10 @@ export function Dialog({ open, defaultOpen, onOpenChange, title, accessibilityTi
177
180
  const visibleRef = useRef(visible);
178
181
  visibleRef.current = visible;
179
182
  const [nativeVisible, setNativeVisible] = useState(visible);
183
+ // Entry focus waits for the native presentation (onShow) so it runs after the enter animation's interaction.
184
+ const [entered, setEntered] = useState(false);
185
+ useEffect(() => { if (!visible)
186
+ setEntered(false); }, [visible]);
180
187
  const keyboardViewport = useModalKeyboardViewport(nativeVisible);
181
188
  const nativeVisibleRef = useRef(visible);
182
189
  nativeVisibleRef.current = nativeVisible;
@@ -361,66 +368,70 @@ export function Dialog({ open, defaultOpen, onOpenChange, title, accessibilityTi
361
368
  const contentBackground = resolveColorReference(dialogRecipe.content.background, palette);
362
369
  const contentBorder = resolveColorReference(dialogRecipe.content.border, palette);
363
370
  const stackActions = environment.textScale >= 1.6 || windowWidth < 480;
364
- return (_jsx(Modal, { ...modalProps, animationType: "none", onDismiss: () => {
365
- nativeShownRef.current = false;
366
- const pending = pendingExitRef.current;
367
- if (pending)
368
- completeExit(pending.token);
369
- }, onRequestClose: () => requestClose("back"), onShow: (event) => {
370
- nativeShownRef.current = true;
371
- startEnter();
372
- onShow?.(event);
373
- }, statusBarTranslucent: true, transparent: true, visible: nativeVisible, children: _jsx(ModalInteractionBoundary, { visible: visible, children: _jsxs(Animated.View, { onLayout: (event) => keyboardViewport.setModalHeight(event.nativeEvent.layout.height), style: {
374
- flex: 1,
375
- justifyContent: "center",
376
- opacity: motionProgress,
377
- ...viewportPadding,
378
- paddingBottom: keyboardViewport.dockedKeyboard
379
- ? spacing.md + keyboardViewport.keyboardInset
380
- : viewportPadding.paddingBottom,
381
- }, children: [_jsx(Scrim, {}), dismissible ? (_jsx(Pressable, { accessible: false, importantForAccessibility: "no-hide-descendants", onPress: () => requestClose("outside"), style: { bottom: 0, left: 0, position: "absolute", right: 0, top: 0 } })) : null, _jsxs(Animated.View, { ref: originContentRef, onLayout: (event) => {
382
- const { width, height } = event.nativeEvent.layout;
383
- const previous = originLayout.current;
384
- originLayout.current = { width, height };
385
- // Error copy, keyboard or rotation may change the destination after entry.
386
- // Fade instead of returning through stale dimensions; a later open remeasures.
387
- if (previous && (previous.width !== width || previous.height !== height))
388
- setOriginTransform(null);
389
- }, accessibilityLabel: [accessibleTitle, description].filter(Boolean).join(", "), accessibilityState: { busy }, accessibilityViewIsModal: true, importantForAccessibility: "yes", role: "dialog", style: [
390
- {
391
- alignSelf: "center",
392
- backgroundColor: contentBackground,
393
- borderColor: contentBorder,
394
- borderRadius: tokens.radius[dialogRecipe.content.radius],
395
- borderWidth: dialogRecipe.content.borderWidth,
396
- ...resolveNativeShadowElevation(surfaceShadow, designProfile !== undefined, 8),
397
- gap: dialogRecipe.content.gap,
398
- maxWidth: sizeRecipe.maxWidth,
399
- maxHeight: "100%",
400
- flexShrink: 1,
401
- padding: sizeRecipe.padding,
402
- shadowColor: surfaceShadow.color,
403
- shadowOffset: { width: 0, height: surfaceShadow.offsetY },
404
- shadowOpacity: surfaceShadow.opacity,
405
- shadowRadius: surfaceShadow.radius,
406
- width: "100%",
407
- },
408
- originTransform && !environment.reducedMotion ? { transform: [
409
- { translateX: motionProgress.interpolate({ inputRange: [0, 1], outputRange: [originTransform.translateX, 0] }) },
410
- { translateY: motionProgress.interpolate({ inputRange: [0, 1], outputRange: [originTransform.translateY, 0] }) },
411
- { scaleX: motionProgress.interpolate({ inputRange: [0, 1], outputRange: [originTransform.scaleX, 1] }) },
412
- { scaleY: motionProgress.interpolate({ inputRange: [0, 1], outputRange: [originTransform.scaleY, 1] }) },
413
- ] } : undefined,
414
- contentStyle,
415
- ], children: [_jsxs(View, { style: { flexShrink: 1, gap: description ? spacing.xs : dialogRecipe.content.gap }, children: [_jsxs(View, { style: {
416
- alignItems: "flex-start",
417
- direction: environment.direction,
418
- flexDirection: "row",
419
- gap: spacing.sm,
420
- }, children: [_jsx(View, { style: { flex: 1 }, children: _jsx(Text, { accessibilityRole: "header", tone: "primary", variant: "title", children: title }) }), dismissible ? (_jsx(IconButton, { disabled: busy, label: closeLabel, onPress: () => requestClose("close-action"), children: _jsx(CloseGlyph, {}) })) : null] }), description || children ? (_jsxs(DialogScrollBody, { gap: dialogRecipe.content.gap, children: [description ? _jsx(Text, { tone: "muted", children: description }) : null, _jsx(KeyboardAvoidanceContext.Provider, { value: true, children: children })] })) : null] }), _jsx(OverlayActions, { busy: busy, onAction: runAction, pendingAction: actionPending, stacked: stackActions, ...(primaryAction === undefined ? {} : { primaryAction }), ...(secondaryAction === undefined ? {} : { secondaryAction }) })] })] }) }) }));
371
+ // Overlay content is not in the screen body: a bleeding rail inside must not reach for the screen gutter.
372
+ return (_jsx(ScreenBodyInsetContext.Provider, { value: 0, children: _jsx(InitialFocusScope, { mode: initialFocus, active: visible && entered, children: _jsx(Modal, { ...modalProps, animationType: "none", onDismiss: () => {
373
+ nativeShownRef.current = false;
374
+ const pending = pendingExitRef.current;
375
+ if (pending)
376
+ completeExit(pending.token);
377
+ }, onRequestClose: () => requestClose("back"), onShow: (event) => {
378
+ nativeShownRef.current = true;
379
+ startEnter();
380
+ setEntered(true);
381
+ onShow?.(event);
382
+ }, statusBarTranslucent: true, transparent: true, visible: nativeVisible, children: _jsx(ModalInteractionBoundary, { visible: visible, children: _jsxs(Animated.View, { onLayout: (event) => keyboardViewport.setModalHeight(event.nativeEvent.layout.height), style: {
383
+ flex: 1,
384
+ justifyContent: "center",
385
+ opacity: motionProgress,
386
+ ...viewportPadding,
387
+ paddingBottom: keyboardViewport.dockedKeyboard
388
+ ? spacing.md + keyboardViewport.keyboardInset
389
+ : viewportPadding.paddingBottom,
390
+ }, children: [_jsx(Scrim, {}), dismissible ? (_jsx(Pressable, { accessible: false, importantForAccessibility: "no-hide-descendants", onPress: () => requestClose("outside"), style: { bottom: 0, left: 0, position: "absolute", right: 0, top: 0 } })) : null, _jsxs(Animated.View, { ref: originContentRef, onLayout: (event) => {
391
+ const { width, height } = event.nativeEvent.layout;
392
+ const previous = originLayout.current;
393
+ originLayout.current = { width, height };
394
+ // Error copy, keyboard or rotation may change the destination after entry.
395
+ // Fade instead of returning through stale dimensions; a later open remeasures.
396
+ if (previous && (previous.width !== width || previous.height !== height))
397
+ setOriginTransform(null);
398
+ }, accessibilityLabel: [accessibleTitle, description].filter(Boolean).join(", "), accessibilityState: { busy }, accessibilityViewIsModal: true, importantForAccessibility: "yes", role: "dialog", style: [
399
+ {
400
+ alignSelf: "center",
401
+ backgroundColor: contentBackground,
402
+ borderColor: contentBorder,
403
+ borderRadius: tokens.radius[dialogRecipe.content.radius],
404
+ borderWidth: dialogRecipe.content.borderWidth,
405
+ ...resolveNativeShadowElevation(surfaceShadow, designProfile !== undefined, 8),
406
+ gap: dialogRecipe.content.gap,
407
+ maxWidth: sizeRecipe.maxWidth,
408
+ maxHeight: "100%",
409
+ flexShrink: 1,
410
+ padding: sizeRecipe.padding,
411
+ shadowColor: surfaceShadow.color,
412
+ shadowOffset: { width: 0, height: surfaceShadow.offsetY },
413
+ shadowOpacity: surfaceShadow.opacity,
414
+ shadowRadius: surfaceShadow.radius,
415
+ width: "100%",
416
+ },
417
+ originTransform && !environment.reducedMotion ? { transform: [
418
+ { translateX: motionProgress.interpolate({ inputRange: [0, 1], outputRange: [originTransform.translateX, 0] }) },
419
+ { translateY: motionProgress.interpolate({ inputRange: [0, 1], outputRange: [originTransform.translateY, 0] }) },
420
+ { scaleX: motionProgress.interpolate({ inputRange: [0, 1], outputRange: [originTransform.scaleX, 1] }) },
421
+ { scaleY: motionProgress.interpolate({ inputRange: [0, 1], outputRange: [originTransform.scaleY, 1] }) },
422
+ ] } : undefined,
423
+ contentStyle,
424
+ ], children: [_jsxs(View, { style: { flexShrink: 1, gap: description ? spacing.xs : dialogRecipe.content.gap }, children: [_jsxs(View, { style: {
425
+ alignItems: "flex-start",
426
+ direction: environment.direction,
427
+ flexDirection: "row",
428
+ gap: spacing.sm,
429
+ }, children: [_jsx(View, { style: { flex: 1 }, children: _jsx(Text, { accessibilityRole: "header", tone: "primary", variant: "title", children: title }) }), dismissible ? (_jsx(IconButton, { disabled: busy, label: closeLabel, onPress: () => requestClose("close-action"), children: _jsx(CloseGlyph, {}) })) : null] }), description || children ? (_jsxs(DialogScrollBody, { gap: dialogRecipe.content.gap, children: [description ? _jsx(Text, { tone: "muted", children: description }) : null, _jsx(KeyboardAvoidanceContext.Provider, { value: true, children: children })] })) : null] }), _jsx(OverlayActions, { busy: busy, onAction: runAction, pendingAction: actionPending, stacked: stackActions, ...(primaryAction === undefined ? {} : { primaryAction }), ...(secondaryAction === undefined ? {} : { secondaryAction }) })] })] }) }) }) }) }));
421
430
  }
422
431
  /** Contract session owns duplicate confirms, busy dismissal, error and settlement. */
423
432
  export function AlertDialog({ open, defaultOpen, onOpenChange, request, size = alertDialogRecipe.defaults.size, returnFocusRef, onResult, contentStyle, onShow, ...modalProps }) {
433
+ // Prompt-style content with one text entry is focused on open; confirm-only dialogs have no field to choose.
434
+ const initialFocus = initialFocusDefaults.overlay;
424
435
  validateAlertDialogRequest(request);
425
436
  const viewportPadding = useDialogViewportPadding();
426
437
  const { colors, environment, palette, tokens, designProfile } = useHjmNativeTheme();
@@ -441,6 +452,10 @@ export function AlertDialog({ open, defaultOpen, onOpenChange, request, size = a
441
452
  const visibleRef = useRef(visible);
442
453
  visibleRef.current = visible;
443
454
  const [nativeVisible, setNativeVisible] = useState(visible);
455
+ // Entry focus waits for the native presentation (onShow) so it runs after the enter animation's interaction.
456
+ const [entered, setEntered] = useState(false);
457
+ useEffect(() => { if (!visible)
458
+ setEntered(false); }, [visible]);
444
459
  const keyboardViewport = useModalKeyboardViewport(nativeVisible);
445
460
  const nativeVisibleRef = useRef(visible);
446
461
  nativeVisibleRef.current = nativeVisible;
@@ -646,71 +661,72 @@ export function AlertDialog({ open, defaultOpen, onOpenChange, request, size = a
646
661
  paddingHorizontal: spacing.md,
647
662
  },
648
663
  ], children: _jsx(Text, { style: { color: confirmContent }, variant: "label", children: request.confirmLabel }) }));
649
- return (_jsx(Modal, { ...modalProps, animationType: "none", onDismiss: () => {
650
- nativeShownRef.current = false;
651
- const pending = pendingExitSessions.current[0];
652
- if (pending)
653
- completeExit(pending.session);
654
- }, onRequestClose: () => cancel("back"), onShow: focusLeastDestructive, statusBarTranslucent: true, transparent: true, visible: nativeVisible, children: _jsx(ModalInteractionBoundary, { visible: visible, children: _jsxs(Animated.View, { onLayout: (event) => keyboardViewport.setModalHeight(event.nativeEvent.layout.height), style: {
655
- flex: 1,
656
- justifyContent: "center",
657
- opacity: motionProgress,
658
- ...viewportPadding,
659
- paddingBottom: keyboardViewport.dockedKeyboard
660
- ? spacing.md + keyboardViewport.keyboardInset
661
- : viewportPadding.paddingBottom,
662
- }, children: [_jsx(Scrim, {}), _jsxs(View, { accessibilityLabel: `${request.title}, ${request.description}`, accessibilityState: { busy }, accessibilityViewIsModal: true, importantForAccessibility: "yes", role: "alertdialog", style: [
663
- {
664
- alignSelf: "center",
665
- backgroundColor: contentBackground,
666
- borderColor: contentBorder,
667
- borderRadius: tokens.radius[alertDialogRecipe.content.radius],
668
- borderWidth: alertDialogRecipe.content.borderWidth,
669
- ...resolveNativeShadowElevation(surfaceShadow, designProfile !== undefined, 8),
670
- gap: alertDialogRecipe.content.gap,
671
- maxWidth: sizeRecipe.maxWidth,
672
- maxHeight: "100%",
673
- flexShrink: 1,
674
- padding: sizeRecipe.padding,
675
- shadowColor: surfaceShadow.color,
676
- shadowOffset: {
677
- width: 0,
678
- height: surfaceShadow.offsetY,
679
- },
680
- shadowOpacity: surfaceShadow.opacity,
681
- shadowRadius: surfaceShadow.radius,
682
- width: "100%",
683
- },
684
- contentStyle,
685
- ], children: [_jsxs(DialogScrollBody, { gap: spacing.xs, children: [_jsx(Text, { accessibilityRole: "header", tone: "primary", variant: "title", children: request.title }), _jsx(Text, { tone: "muted", children: request.description }), error ? (_jsx(Text, { accessibilityLiveRegion: "assertive", accessibilityRole: "alert", style: { color: errorColor }, children: error })) : null] }), _jsxs(View, { style: {
686
- direction: environment.direction,
687
- flexDirection: stackActions ? "column" : "row",
688
- gap: stackActions
689
- ? alertDialogRecipe.actions.stackedGap
690
- : alertDialogRecipe.actions.gap,
691
- }, children: [stackActions ? confirmAction : null, request.mode === "confirm" ? (_jsx(Pressable, { ref: cancelRef, accessibilityLabel: request.cancelLabel, accessibilityRole: "button", accessibilityState: { disabled: busy }, disabled: busy, onPress: () => cancel("cancel-action"), style: ({ pressed }) => [
692
- minimumTargetStyle,
693
- {
694
- alignItems: "center",
695
- // The cancel action is a quiet outline on the dialog surface. surfaceAlt
696
- // turned it into a tinted slab wherever a product brands that token
697
- // (BurnTok's pale blue, 2026-09-27), competing with the confirm fill.
698
- backgroundColor: colors.bg,
699
- borderColor: colors.border,
700
- borderRadius: tokens.radius.md,
701
- borderWidth: 1,
702
- flex: stackActions ? undefined : 1,
703
- justifyContent: "center",
704
- minWidth: alertDialogRecipe.actions.minButtonWidth,
705
- opacity: busy ? 0.5 : pressed ? 0.86 : 1,
706
- paddingHorizontal: spacing.md,
707
- },
708
- ], children: _jsx(Text, { variant: "label", children: request.cancelLabel }) })) : null, stackActions ? null : confirmAction] })] })] }) }) }));
664
+ // Overlay content is not in the screen body: a bleeding rail inside must not reach for the screen gutter.
665
+ return (_jsx(ScreenBodyInsetContext.Provider, { value: 0, children: _jsx(InitialFocusScope, { mode: initialFocus, active: visible && entered, children: _jsx(Modal, { ...modalProps, animationType: "none", onDismiss: () => {
666
+ nativeShownRef.current = false;
667
+ const pending = pendingExitSessions.current[0];
668
+ if (pending)
669
+ completeExit(pending.session);
670
+ }, onRequestClose: () => cancel("back"), onShow: (event) => { focusLeastDestructive(event); setEntered(true); }, statusBarTranslucent: true, transparent: true, visible: nativeVisible, children: _jsx(ModalInteractionBoundary, { visible: visible, children: _jsxs(Animated.View, { onLayout: (event) => keyboardViewport.setModalHeight(event.nativeEvent.layout.height), style: {
671
+ flex: 1,
672
+ justifyContent: "center",
673
+ opacity: motionProgress,
674
+ ...viewportPadding,
675
+ paddingBottom: keyboardViewport.dockedKeyboard
676
+ ? spacing.md + keyboardViewport.keyboardInset
677
+ : viewportPadding.paddingBottom,
678
+ }, children: [_jsx(Scrim, {}), _jsxs(View, { accessibilityLabel: `${request.title}, ${request.description}`, accessibilityState: { busy }, accessibilityViewIsModal: true, importantForAccessibility: "yes", role: "alertdialog", style: [
679
+ {
680
+ alignSelf: "center",
681
+ backgroundColor: contentBackground,
682
+ borderColor: contentBorder,
683
+ borderRadius: tokens.radius[alertDialogRecipe.content.radius],
684
+ borderWidth: alertDialogRecipe.content.borderWidth,
685
+ ...resolveNativeShadowElevation(surfaceShadow, designProfile !== undefined, 8),
686
+ gap: alertDialogRecipe.content.gap,
687
+ maxWidth: sizeRecipe.maxWidth,
688
+ maxHeight: "100%",
689
+ flexShrink: 1,
690
+ padding: sizeRecipe.padding,
691
+ shadowColor: surfaceShadow.color,
692
+ shadowOffset: {
693
+ width: 0,
694
+ height: surfaceShadow.offsetY,
695
+ },
696
+ shadowOpacity: surfaceShadow.opacity,
697
+ shadowRadius: surfaceShadow.radius,
698
+ width: "100%",
699
+ },
700
+ contentStyle,
701
+ ], children: [_jsxs(DialogScrollBody, { gap: spacing.xs, children: [_jsx(Text, { accessibilityRole: "header", tone: "primary", variant: "title", children: request.title }), _jsx(Text, { tone: "muted", children: request.description }), error ? (_jsx(Text, { accessibilityLiveRegion: "assertive", accessibilityRole: "alert", style: { color: errorColor }, children: error })) : null] }), _jsxs(View, { style: {
702
+ direction: environment.direction,
703
+ flexDirection: stackActions ? "column" : "row",
704
+ gap: stackActions
705
+ ? alertDialogRecipe.actions.stackedGap
706
+ : alertDialogRecipe.actions.gap,
707
+ }, children: [stackActions ? confirmAction : null, request.mode === "confirm" ? (_jsx(Pressable, { ref: cancelRef, accessibilityLabel: request.cancelLabel, accessibilityRole: "button", accessibilityState: { disabled: busy }, disabled: busy, onPress: () => cancel("cancel-action"), style: ({ pressed }) => [
708
+ minimumTargetStyle,
709
+ {
710
+ alignItems: "center",
711
+ // The cancel action is a quiet outline on the dialog surface. surfaceAlt
712
+ // turned it into a tinted slab wherever a product brands that token
713
+ // (BurnTok's pale blue, 2026-09-27), competing with the confirm fill.
714
+ backgroundColor: colors.bg,
715
+ borderColor: colors.border,
716
+ borderRadius: tokens.radius.md,
717
+ borderWidth: 1,
718
+ flex: stackActions ? undefined : 1,
719
+ justifyContent: "center",
720
+ minWidth: alertDialogRecipe.actions.minButtonWidth,
721
+ opacity: busy ? 0.5 : pressed ? 0.86 : 1,
722
+ paddingHorizontal: spacing.md,
723
+ },
724
+ ], children: _jsx(Text, { variant: "label", children: request.cancelLabel }) })) : null, stackActions ? null : confirmAction] })] })] }) }) }) }) }));
709
725
  }
710
726
  /** Native Sheet applies policy before emitting a concrete dismissal reason. */
711
727
  export function Sheet({ open, defaultOpen, onOpenChange, title, accessibilityTitle, description, children, footer, placement = "bottom", size = sheetRecipe.defaults.size, busy = false, dismissPolicy, closeLabel, returnFocusRef, safeAreaInsets: suppliedSafeAreaInsets, onDismissComplete, contentStyle,
712
728
  // Default on since the 2026-10-16 keyboard audit: input sheets that forgot the opt-in were covered.
713
- keyboardAvoidance = true, scrollable = false, showClose, onShow, ...modalProps }) {
729
+ keyboardAvoidance = true, scrollable = false, showClose, onShow, initialFocus = initialFocusDefaults.overlay, ...modalProps }) {
714
730
  const { environment, palette, tokens, designProfile } = useHjmNativeTheme();
715
731
  // The profile floating role is shared with Web. Modal lifecycle, inset and
716
732
  // action state remain recipe-owned; changing decoration must not replace them.
@@ -739,6 +755,10 @@ keyboardAvoidance = true, scrollable = false, showClose, onShow, ...modalProps }
739
755
  const visibleRef = useRef(visible);
740
756
  visibleRef.current = visible;
741
757
  const [nativeVisible, setNativeVisible] = useState(visible);
758
+ // Entry focus waits for the native presentation (onShow) so it runs after the enter animation's interaction.
759
+ const [entered, setEntered] = useState(false);
760
+ useEffect(() => { if (!visible)
761
+ setEntered(false); }, [visible]);
742
762
  const { viewportHeight, keyboardInset, dockedKeyboard, setModalHeight } = useModalKeyboardViewport(nativeVisible, keyboardAvoidance);
743
763
  const nativeVisibleRef = useRef(visible);
744
764
  nativeVisibleRef.current = nativeVisible;
@@ -953,93 +973,95 @@ keyboardAvoidance = true, scrollable = false, showClose, onShow, ...modalProps }
953
973
  const sheetBodyRoom = listRowScrollRoom(sheetRecipe.content.paddingHorizontal);
954
974
  const contentBackground = resolveColorReference(sheetRecipe.content.background, palette);
955
975
  const contentBorder = resolveColorReference(sheetRecipe.content.border, palette);
956
- return (_jsx(Modal, { ...modalProps, animationType: "none", onDismiss: () => {
957
- nativeShownRef.current = false;
958
- const pending = pendingDismissals.current[0];
959
- if (pending)
960
- completeDismiss(pending.cycle);
961
- }, onRequestClose: () => requestClose("back"), onShow: (event) => {
962
- nativeShownRef.current = true;
963
- startEnter();
964
- onShow?.(event);
965
- }, statusBarTranslucent: true, transparent: true, visible: nativeVisible, children: _jsx(ModalInteractionBoundary, { visible: visible, children: _jsxs(Animated.View, { onLayout: (event) => setModalHeight(event.nativeEvent.layout.height), style: {
966
- alignItems: physicalPlacement === "right" ? "flex-end" : "flex-start",
967
- flex: 1,
968
- justifyContent: physicalPlacement === "bottom" ? "flex-end" : "flex-start",
969
- opacity: motionProgress,
970
- paddingTop: insets.top,
971
- paddingBottom: keyboardInset,
972
- }, children: [_jsx(Scrim, {}), policy.dismissible && policy.outsideDismiss ? (
973
- // Backdrop press closes every sheet by default (2026-10-17 user decision) through the same
974
- // requestClose path as the handle, so busy and dismissPolicy guards apply. Named by closeLabel.
975
- _jsx(Pressable, { accessibilityLabel: closeLabel, accessibilityRole: "button", accessibilityState: { disabled: closeBlocked }, disabled: closeBlocked, onPress: () => requestClose("outside"), style: { bottom: 0, left: 0, position: "absolute", right: 0, top: 0 } })) : null, _jsxs(Animated.View, { accessibilityLabel: [accessibleTitle, description].filter(Boolean).join(", "), accessibilityState: { busy }, accessibilityViewIsModal: true, importantForAccessibility: "yes", onAccessibilityEscape: () => requestClose("escape"), onLayout: (event) => { sheetHeight.current = event.nativeEvent.layout.height; }, role: "dialog", style: [
976
- {
977
- backgroundColor: contentBackground,
978
- borderColor: contentBorder,
979
- borderRadius: tokens.radius[sheetRecipe.content.radius],
980
- borderBottomLeftRadius: physicalPlacement === "bottom" ? 0 : tokens.radius[sheetRecipe.content.radius],
981
- borderBottomRightRadius: physicalPlacement === "bottom" ? 0 : tokens.radius[sheetRecipe.content.radius],
982
- borderWidth: sheetRecipe.content.borderWidth,
983
- ...resolveNativeShadowElevation(surfaceShadow, designProfile !== undefined, 8),
984
- gap: sheetRecipe.body.gap,
985
- // A fixed size ratio drives the height; `auto` lets the content decide
986
- // and the recipe's maxHeightRatio caps it.
987
- height: side
988
- ? availableHeight
989
- : sizeRatio === null
990
- ? undefined
991
- : Math.min(viewportHeight * sizeRatio, availableHeight),
992
- maxWidth: side ? 420 : undefined,
993
- // maxHeightRatio caps only `auto`. A fixed size is already bounded by availableHeight
994
- // (viewport − top inset − keyboard); capping it too stopped `full` at 90% (2026-10-06 follow-up).
995
- maxHeight: side || sizeRatio !== null
996
- ? availableHeight
997
- : Math.min(availableHeight, viewportHeight * sheetRecipe.content.maxHeightRatio),
998
- paddingBottom: sheetRecipe.content.paddingBottom + bottomInset,
999
- paddingLeft: sheetRecipe.content.paddingHorizontal + insets.left,
1000
- paddingRight: sheetRecipe.content.paddingHorizontal + insets.right,
1001
- // The top safe area limits the viewport, rather than adding a second
1002
- // notch-sized gap inside a sheet anchored to the bottom.
1003
- paddingTop: sheetRecipe.content.paddingTop,
1004
- shadowColor: surfaceShadow.color,
1005
- shadowOffset: {
1006
- width: 0,
1007
- height: surfaceShadow.offsetY,
1008
- },
1009
- shadowOpacity: surfaceShadow.opacity,
1010
- shadowRadius: surfaceShadow.radius,
1011
- transform: environment.reducedMotion
1012
- ? undefined
1013
- : side
1014
- ? [{ translateX: translate }]
1015
- // Two translateY entries compose: the enter/exit motion plus the live drag offset.
1016
- : [{ translateY: translate }, { translateY: dragOffset }],
1017
- width: side ? "88%" : "100%",
1018
- },
1019
- contentStyle,
1020
- ], children: [_jsx(View, { accessible: true, accessibilityRole: "header", accessibilityLabel: accessibleTitle, style: { position: "absolute", top: 0, start: 0, width: 1, height: 1, overflow: "hidden" } }), _jsxs(View, { ...(canSwipe ? dragResponder.panHandlers : {}), style: { flexShrink: 0 }, children: [showHandle && sheetRecipe.handle.visibleByDefault ? (
1021
- // The handle doubles as a close control so switch and screen-reader users need no drag.
1022
- _jsx(Pressable, { accessibilityRole: "button", accessibilityLabel: closeLabel, accessibilityState: { disabled: closeBlocked }, disabled: closeBlocked, onPress: () => requestClose("close-action"), hitSlop: spacing.sm, style: { alignSelf: "center", paddingVertical: spacing.xs }, children: _jsx(View, { style: { width: sheetRecipe.handle.width, height: sheetRecipe.handle.height, borderRadius: tokens.radius[sheetRecipe.handle.radius], backgroundColor: resolveColorReference(sheetRecipe.handle.color, palette) } }) })) : null, description || (policy.dismissible && closeVisible) ? (_jsxs(View, { style: {
1023
- alignItems: "center",
1024
- minHeight: sheetRecipe.header.minHeight,
976
+ // Overlay content is not in the screen body: a bleeding rail inside must not reach for the screen gutter.
977
+ return (_jsx(ScreenBodyInsetContext.Provider, { value: 0, children: _jsx(InitialFocusScope, { mode: initialFocus, active: visible && entered, children: _jsx(Modal, { ...modalProps, animationType: "none", onDismiss: () => {
978
+ nativeShownRef.current = false;
979
+ const pending = pendingDismissals.current[0];
980
+ if (pending)
981
+ completeDismiss(pending.cycle);
982
+ }, onRequestClose: () => requestClose("back"), onShow: (event) => {
983
+ nativeShownRef.current = true;
984
+ startEnter();
985
+ setEntered(true);
986
+ onShow?.(event);
987
+ }, statusBarTranslucent: true, transparent: true, visible: nativeVisible, children: _jsx(ModalInteractionBoundary, { visible: visible, children: _jsxs(Animated.View, { onLayout: (event) => setModalHeight(event.nativeEvent.layout.height), style: {
988
+ alignItems: physicalPlacement === "right" ? "flex-end" : "flex-start",
989
+ flex: 1,
990
+ justifyContent: physicalPlacement === "bottom" ? "flex-end" : "flex-start",
991
+ opacity: motionProgress,
992
+ paddingTop: insets.top,
993
+ paddingBottom: keyboardInset,
994
+ }, children: [_jsx(Scrim, {}), policy.dismissible && policy.outsideDismiss ? (
995
+ // Backdrop press closes every sheet by default (2026-10-17 user decision) through the same
996
+ // requestClose path as the handle, so busy and dismissPolicy guards apply. Named by closeLabel.
997
+ _jsx(Pressable, { accessibilityLabel: closeLabel, accessibilityRole: "button", accessibilityState: { disabled: closeBlocked }, disabled: closeBlocked, onPress: () => requestClose("outside"), style: { bottom: 0, left: 0, position: "absolute", right: 0, top: 0 } })) : null, _jsxs(Animated.View, { accessibilityLabel: [accessibleTitle, description].filter(Boolean).join(", "), accessibilityState: { busy }, accessibilityViewIsModal: true, importantForAccessibility: "yes", onAccessibilityEscape: () => requestClose("escape"), onLayout: (event) => { sheetHeight.current = event.nativeEvent.layout.height; }, role: "dialog", style: [
998
+ {
999
+ backgroundColor: contentBackground,
1000
+ borderColor: contentBorder,
1001
+ borderRadius: tokens.radius[sheetRecipe.content.radius],
1002
+ borderBottomLeftRadius: physicalPlacement === "bottom" ? 0 : tokens.radius[sheetRecipe.content.radius],
1003
+ borderBottomRightRadius: physicalPlacement === "bottom" ? 0 : tokens.radius[sheetRecipe.content.radius],
1004
+ borderWidth: sheetRecipe.content.borderWidth,
1005
+ ...resolveNativeShadowElevation(surfaceShadow, designProfile !== undefined, 8),
1006
+ gap: sheetRecipe.body.gap,
1007
+ // A fixed size ratio drives the height; `auto` lets the content decide
1008
+ // and the recipe's maxHeightRatio caps it.
1009
+ height: side
1010
+ ? availableHeight
1011
+ : sizeRatio === null
1012
+ ? undefined
1013
+ : Math.min(viewportHeight * sizeRatio, availableHeight),
1014
+ maxWidth: side ? 420 : undefined,
1015
+ // maxHeightRatio caps only `auto`. A fixed size is already bounded by availableHeight
1016
+ // (viewport − top inset − keyboard); capping it too stopped `full` at 90% (2026-10-06 follow-up).
1017
+ maxHeight: side || sizeRatio !== null
1018
+ ? availableHeight
1019
+ : Math.min(availableHeight, viewportHeight * sheetRecipe.content.maxHeightRatio),
1020
+ paddingBottom: sheetRecipe.content.paddingBottom + bottomInset,
1021
+ paddingLeft: sheetRecipe.content.paddingHorizontal + insets.left,
1022
+ paddingRight: sheetRecipe.content.paddingHorizontal + insets.right,
1023
+ // The top safe area limits the viewport, rather than adding a second
1024
+ // notch-sized gap inside a sheet anchored to the bottom.
1025
+ paddingTop: sheetRecipe.content.paddingTop,
1026
+ shadowColor: surfaceShadow.color,
1027
+ shadowOffset: {
1028
+ width: 0,
1029
+ height: surfaceShadow.offsetY,
1030
+ },
1031
+ shadowOpacity: surfaceShadow.opacity,
1032
+ shadowRadius: surfaceShadow.radius,
1033
+ transform: environment.reducedMotion
1034
+ ? undefined
1035
+ : side
1036
+ ? [{ translateX: translate }]
1037
+ // Two translateY entries compose: the enter/exit motion plus the live drag offset.
1038
+ : [{ translateY: translate }, { translateY: dragOffset }],
1039
+ width: side ? "88%" : "100%",
1040
+ },
1041
+ contentStyle,
1042
+ ], children: [_jsx(View, { accessible: true, accessibilityRole: "header", accessibilityLabel: accessibleTitle, style: { position: "absolute", top: 0, start: 0, width: 1, height: 1, overflow: "hidden" } }), _jsxs(View, { ...(canSwipe ? dragResponder.panHandlers : {}), style: { flexShrink: 0 }, children: [showHandle && sheetRecipe.handle.visibleByDefault ? (
1043
+ // The handle doubles as a close control so switch and screen-reader users need no drag.
1044
+ _jsx(Pressable, { accessibilityRole: "button", accessibilityLabel: closeLabel, accessibilityState: { disabled: closeBlocked }, disabled: closeBlocked, onPress: () => requestClose("close-action"), hitSlop: spacing.sm, style: { alignSelf: "center", paddingVertical: spacing.xs }, children: _jsx(View, { style: { width: sheetRecipe.handle.width, height: sheetRecipe.handle.height, borderRadius: tokens.radius[sheetRecipe.handle.radius], backgroundColor: resolveColorReference(sheetRecipe.handle.color, palette) } }) })) : null, description || (policy.dismissible && closeVisible) ? (_jsxs(View, { style: {
1045
+ alignItems: "center",
1046
+ minHeight: sheetRecipe.header.minHeight,
1047
+ flexShrink: 0,
1048
+ direction: environment.direction,
1049
+ flexDirection: "row",
1050
+ // Without a description the × keeps its inline-end place (2026-10-17: no visible title).
1051
+ justifyContent: "flex-end",
1052
+ gap: spacing.sm,
1053
+ }, children: [description ? _jsx(View, { style: { flex: 1 }, children: _jsx(Text, { tone: "muted", children: description }) }) : null, policy.dismissible && closeVisible ? (_jsx(IconButton, { disabled: busy && !policy.dismissWhileBusy, label: closeLabel, onPress: () => requestClose("close-action"), children: _jsx(CloseGlyph, {}) })) : null] })) : null] }), scrollable ? (_jsx(ScrollView
1054
+ // The sheet's inline padding used to sit outside this clipping viewport, so ListRows cut their surface
1055
+ // bleed. The viewport now reaches the padding edge and carries the padding inside its content (Web's
1056
+ // body scroll box does the same); content keeps its position (2026-10-17 1.27.2 adoption).
1057
+ , {
1058
+ // The sheet's inline padding used to sit outside this clipping viewport, so ListRows cut their surface
1059
+ // bleed. The viewport now reaches the padding edge and carries the padding inside its content (Web's
1060
+ // body scroll box does the same); content keeps its position (2026-10-17 1.27.2 adoption).
1061
+ style: { flexShrink: 1, minHeight: 0, ...bodyFill, ...sheetBodyRoom.viewport }, contentContainerStyle: { gap: sheetRecipe.body.gap, ...sheetBodyRoom.content }, keyboardShouldPersistTaps: "handled", keyboardDismissMode: Platform.OS === "ios" ? "interactive" : "on-drag", automaticallyAdjustKeyboardInsets: false, children: _jsx(KeyboardAvoidanceContext.Provider, { value: keyboardAvoidance, children: children }) })) : _jsx(View, { style: { gap: sheetRecipe.body.gap, flexShrink: 1, ...bodyFill }, children: _jsx(KeyboardAvoidanceContext.Provider, { value: keyboardAvoidance, children: children }) }), footer ? (_jsx(View, { style: {
1062
+ gap: sheetRecipe.footer.gap,
1025
1063
  flexShrink: 0,
1026
- direction: environment.direction,
1027
- flexDirection: "row",
1028
- // Without a description the × keeps its inline-end place (2026-10-17: no visible title).
1029
- justifyContent: "flex-end",
1030
- gap: spacing.sm,
1031
- }, children: [description ? _jsx(View, { style: { flex: 1 }, children: _jsx(Text, { tone: "muted", children: description }) }) : null, policy.dismissible && closeVisible ? (_jsx(IconButton, { disabled: busy && !policy.dismissWhileBusy, label: closeLabel, onPress: () => requestClose("close-action"), children: _jsx(CloseGlyph, {}) })) : null] })) : null] }), scrollable ? (_jsx(ScrollView
1032
- // The sheet's inline padding used to sit outside this clipping viewport, so ListRows cut their surface
1033
- // bleed. The viewport now reaches the padding edge and carries the padding inside its content (Web's
1034
- // body scroll box does the same); content keeps its position (2026-10-17 1.27.2 adoption).
1035
- , {
1036
- // The sheet's inline padding used to sit outside this clipping viewport, so ListRows cut their surface
1037
- // bleed. The viewport now reaches the padding edge and carries the padding inside its content (Web's
1038
- // body scroll box does the same); content keeps its position (2026-10-17 1.27.2 adoption).
1039
- style: { flexShrink: 1, minHeight: 0, ...bodyFill, ...sheetBodyRoom.viewport }, contentContainerStyle: { gap: sheetRecipe.body.gap, ...sheetBodyRoom.content }, keyboardShouldPersistTaps: "handled", keyboardDismissMode: Platform.OS === "ios" ? "interactive" : "on-drag", automaticallyAdjustKeyboardInsets: false, children: _jsx(KeyboardAvoidanceContext.Provider, { value: keyboardAvoidance, children: children }) })) : _jsx(View, { style: { gap: sheetRecipe.body.gap, flexShrink: 1, ...bodyFill }, children: _jsx(KeyboardAvoidanceContext.Provider, { value: keyboardAvoidance, children: children }) }), footer ? (_jsx(View, { style: {
1040
- gap: sheetRecipe.footer.gap,
1041
- flexShrink: 0,
1042
- paddingTop: sheetRecipe.footer.paddingTop,
1043
- }, children: footer })) : null] })] }) }) }));
1064
+ paddingTop: sheetRecipe.footer.paddingTop,
1065
+ }, children: footer })) : null] })] }) }) }) }) }));
1044
1066
  }
1045
1067
  //# sourceMappingURL=overlays.js.map