@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.
- package/dist/auth-screen.d.ts +13 -2
- package/dist/auth-screen.d.ts.map +1 -1
- package/dist/auth-screen.js +21 -7
- package/dist/auth-screen.js.map +1 -1
- package/dist/collection-rail.d.ts +7 -1
- package/dist/collection-rail.d.ts.map +1 -1
- package/dist/collection-rail.js +12 -5
- package/dist/collection-rail.js.map +1 -1
- package/dist/evidence.d.ts +1 -1
- package/dist/evidence.js +1 -1
- package/dist/feedback.d.ts +7 -1
- package/dist/feedback.d.ts.map +1 -1
- package/dist/feedback.js +4 -2
- package/dist/feedback.js.map +1 -1
- package/dist/inputs.d.ts +6 -0
- package/dist/inputs.d.ts.map +1 -1
- package/dist/inputs.js +4 -1
- package/dist/inputs.js.map +1 -1
- package/dist/internal/initial-focus.d.ts +11 -0
- package/dist/internal/initial-focus.d.ts.map +1 -0
- package/dist/internal/initial-focus.js +60 -0
- package/dist/internal/initial-focus.js.map +1 -0
- package/dist/internal/list-row-scroll-room.d.ts +2 -1
- package/dist/internal/list-row-scroll-room.d.ts.map +1 -1
- package/dist/internal/list-row-scroll-room.js +2 -1
- package/dist/internal/list-row-scroll-room.js.map +1 -1
- package/dist/internal/screen-body-inset.d.ts +9 -0
- package/dist/internal/screen-body-inset.d.ts.map +1 -0
- package/dist/internal/screen-body-inset.js +10 -0
- package/dist/internal/screen-body-inset.js.map +1 -0
- package/dist/keyboard.d.ts +1 -1
- package/dist/keyboard.d.ts.map +1 -1
- package/dist/keyboard.js.map +1 -1
- package/dist/overlays.d.ts +10 -2
- package/dist/overlays.d.ts.map +1 -1
- package/dist/overlays.js +228 -206
- package/dist/overlays.js.map +1 -1
- package/dist/screen-flows.d.ts +9 -4
- package/dist/screen-flows.d.ts.map +1 -1
- package/dist/screen-flows.js +12 -9
- package/dist/screen-flows.js.map +1 -1
- package/dist/screens.d.ts +23 -2
- package/dist/screens.d.ts.map +1 -1
- package/dist/screens.js +21 -6
- package/dist/screens.js.map +1 -1
- package/package.json +2 -2
package/dist/overlays.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"overlays.d.ts","sourceRoot":"","sources":["../src/overlays.tsx"],"names":[],"mappings":"
|
|
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
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
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
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
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
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
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
|
-
|
|
1027
|
-
|
|
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
|