@hjmds/react-native 1.29.0 → 1.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth-screen.d.ts +10 -1
- package/dist/auth-screen.d.ts.map +1 -1
- package/dist/auth-screen.js +39 -24
- package/dist/auth-screen.js.map +1 -1
- package/dist/carousel.d.ts +25 -1
- package/dist/carousel.d.ts.map +1 -1
- package/dist/carousel.js +18 -4
- package/dist/carousel.js.map +1 -1
- package/dist/data-display.d.ts +5 -1
- package/dist/data-display.d.ts.map +1 -1
- package/dist/data-display.js +28 -14
- package/dist/data-display.js.map +1 -1
- package/dist/data-table.d.ts.map +1 -1
- package/dist/data-table.js +13 -8
- package/dist/data-table.js.map +1 -1
- package/dist/evidence.d.ts +3 -1562
- package/dist/evidence.d.ts.map +1 -1
- package/dist/evidence.js +93 -255
- package/dist/evidence.js.map +1 -1
- package/dist/forms.js +3 -3
- package/dist/forms.js.map +1 -1
- package/dist/icon-svg.d.ts +8 -0
- package/dist/icon-svg.d.ts.map +1 -0
- package/dist/icon-svg.js +22 -0
- package/dist/icon-svg.js.map +1 -0
- package/dist/image-choice.d.ts +5 -0
- package/dist/image-choice.d.ts.map +1 -1
- package/dist/image-choice.js +5 -2
- package/dist/image-choice.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/inputs.d.ts.map +1 -1
- package/dist/inputs.js +2 -1
- package/dist/inputs.js.map +1 -1
- package/dist/internal/bottom-navigation-inset.d.ts +21 -0
- package/dist/internal/bottom-navigation-inset.d.ts.map +1 -0
- package/dist/internal/bottom-navigation-inset.js +28 -0
- package/dist/internal/bottom-navigation-inset.js.map +1 -0
- package/dist/internal/field-frame.d.ts +11 -1
- package/dist/internal/field-frame.d.ts.map +1 -1
- package/dist/internal/field-frame.js +17 -1
- package/dist/internal/field-frame.js.map +1 -1
- package/dist/internal/initial-focus.d.ts +2 -1
- package/dist/internal/initial-focus.d.ts.map +1 -1
- package/dist/internal/initial-focus.js +11 -3
- package/dist/internal/initial-focus.js.map +1 -1
- package/dist/internal/media-album.d.ts +48 -0
- package/dist/internal/media-album.d.ts.map +1 -0
- package/dist/internal/media-album.js +36 -0
- package/dist/internal/media-album.js.map +1 -0
- package/dist/internal/message-reactions.d.ts +28 -2
- package/dist/internal/message-reactions.d.ts.map +1 -1
- package/dist/internal/message-reactions.js +25 -6
- package/dist/internal/message-reactions.js.map +1 -1
- package/dist/navigation.d.ts +12 -2
- package/dist/navigation.d.ts.map +1 -1
- package/dist/navigation.js +75 -19
- package/dist/navigation.js.map +1 -1
- package/dist/number-field.d.ts.map +1 -1
- package/dist/number-field.js +2 -0
- package/dist/number-field.js.map +1 -1
- package/dist/overlays.d.ts +39 -1
- package/dist/overlays.d.ts.map +1 -1
- package/dist/overlays.js +254 -205
- package/dist/overlays.js.map +1 -1
- package/dist/primitives.d.ts +29 -1
- package/dist/primitives.d.ts.map +1 -1
- package/dist/primitives.js +18 -3
- package/dist/primitives.js.map +1 -1
- package/dist/public-exports.generated.d.ts +209 -0
- package/dist/public-exports.generated.d.ts.map +1 -0
- package/dist/public-exports.generated.js +642 -0
- package/dist/public-exports.generated.js.map +1 -0
- package/dist/screen-flows.d.ts +104 -12
- package/dist/screen-flows.d.ts.map +1 -1
- package/dist/screen-flows.js +87 -17
- package/dist/screen-flows.js.map +1 -1
- package/dist/screens.d.ts +59 -7
- package/dist/screens.d.ts.map +1 -1
- package/dist/screens.js +89 -19
- package/dist/screens.js.map +1 -1
- package/dist/steps.d.ts +7 -2
- package/dist/steps.d.ts.map +1 -1
- package/dist/steps.js +12 -5
- package/dist/steps.js.map +1 -1
- package/package.json +9 -3
package/dist/overlays.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { ScreenBodyInsetContext } from "./internal/screen-body-inset.js";
|
|
3
|
+
// Overlays cover the bottom bar, so screens inside them must not pad for it.
|
|
4
|
+
import { BottomNavigationInsetContext } from "./internal/bottom-navigation-inset.js";
|
|
3
5
|
import { InitialFocusProvider, useInitialFocusScope, useOverlayLayer } from "./internal/initial-focus.js";
|
|
4
6
|
import { initialFocusDefaults } from "@hjmds/design-contracts/screen-patterns";
|
|
5
7
|
import { resolveOriginTransition } from "@hjmds/design-contracts/content-transition";
|
|
@@ -12,7 +14,7 @@ import { resolveColorReference } from "@hjmds/design-contracts/color-references"
|
|
|
12
14
|
import { backdrop, easing, glyph, spacing } from "@hjmds/design-contracts/foundations";
|
|
13
15
|
import { actionGroupRecipe, alertDialogRecipe, dialogRecipe, sheetRecipe, } from "@hjmds/design-contracts/recipes";
|
|
14
16
|
import { useCallback, useEffect, useRef, useState, useSyncExternalStore, } from "react";
|
|
15
|
-
import { Animated, Easing, Keyboard, Modal, PanResponder, Platform, ScrollView, Pressable, View, useWindowDimensions, } from "react-native";
|
|
17
|
+
import { Animated, Easing, Keyboard, Modal, PanResponder, Platform, ScrollView, Pressable, StatusBar, View, useWindowDimensions, } from "react-native";
|
|
16
18
|
import { Button, IconButton } from "./actions.js";
|
|
17
19
|
import { scheduleAfterNativeModalTeardown, shouldAwaitNativeModalDismiss, } from "./internal/modal-lifecycle.js";
|
|
18
20
|
import { isDevelopment } from "./internal/diagnostics.js";
|
|
@@ -35,8 +37,8 @@ function ModalInteractionBoundary({ visible, onTouchStart, children }) {
|
|
|
35
37
|
// BT-QA-027: maximum text made a centered BurnTok confirmation taller than the
|
|
36
38
|
// window, putting its title/cancel off-screen. Keep full copy in a bounded scroll
|
|
37
39
|
// and actions outside it; capping fonts or clipping copy would hide the decision.
|
|
38
|
-
function DialogScrollBody({ children, gap }) {
|
|
39
|
-
return _jsx(ScrollView, { style: { flexGrow: 0, flexShrink: 1 }, contentContainerStyle: { gap }, keyboardShouldPersistTaps: "handled",
|
|
40
|
+
function DialogScrollBody({ children, gap, scrollEnabled = true }) {
|
|
41
|
+
return _jsx(ScrollView, { scrollEnabled: scrollEnabled, style: { flexGrow: 0, flexShrink: 1 }, contentContainerStyle: { gap }, keyboardShouldPersistTaps: "handled",
|
|
40
42
|
// Positioner owns safe-area clearance.
|
|
41
43
|
automaticallyAdjustContentInsets: false, automaticallyAdjustKeyboardInsets: false, children: children });
|
|
42
44
|
}
|
|
@@ -152,7 +154,7 @@ function resolveOverlayAccessibleTitle(title, accessibilityTitle) {
|
|
|
152
154
|
throw new TypeError("An element `title` requires `accessibilityTitle` for the modal accessible name");
|
|
153
155
|
}
|
|
154
156
|
/** Native modal boundary with one reasoned close intent for each user attempt. */
|
|
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 }) {
|
|
157
|
+
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, overlayLayer, scrollEnabled = true, ...modalProps }) {
|
|
156
158
|
const [actionPending, setActionPending] = useState(null);
|
|
157
159
|
const actionRun = useRef(null);
|
|
158
160
|
const busy = externalBusy || actionPending !== null;
|
|
@@ -377,64 +379,64 @@ export function Dialog({ open, defaultOpen, onOpenChange, title, accessibilityTi
|
|
|
377
379
|
const contentBorder = resolveColorReference(dialogRecipe.content.border, palette);
|
|
378
380
|
const stackActions = environment.textScale >= 1.6 || windowWidth < 480;
|
|
379
381
|
// Overlay content is not in the screen body: a bleeding rail inside must not reach for the screen gutter.
|
|
380
|
-
return (_jsx(ScreenBodyInsetContext.Provider, { value: 0, children: _jsx(InitialFocusProvider, { scope: focusScope, children: _jsx(Modal, { ...modalProps, animationType: "none", onDismiss: () => {
|
|
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
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
382
|
+
return (_jsx(ScreenBodyInsetContext.Provider, { value: 0, children: _jsx(BottomNavigationInsetContext.Provider, { value: 0, children: _jsx(InitialFocusProvider, { scope: focusScope, children: _jsx(Modal, { ...modalProps, animationType: "none", onDismiss: () => {
|
|
383
|
+
nativeShownRef.current = false;
|
|
384
|
+
const pending = pendingExitRef.current;
|
|
385
|
+
if (pending)
|
|
386
|
+
completeExit(pending.token);
|
|
387
|
+
}, onRequestClose: () => requestClose("back"), onShow: (event) => {
|
|
388
|
+
nativeShownRef.current = true;
|
|
389
|
+
startEnter();
|
|
390
|
+
setEntered(true);
|
|
391
|
+
onShow?.(event);
|
|
392
|
+
}, statusBarTranslucent: true, transparent: true, visible: nativeVisible, children: _jsxs(ModalInteractionBoundary, { visible: visible, onTouchStart: focusScope.onTouchStart, children: [_jsxs(Animated.View, { onLayout: (event) => keyboardViewport.setModalHeight(event.nativeEvent.layout.height), style: {
|
|
393
|
+
flex: 1,
|
|
394
|
+
justifyContent: "center",
|
|
395
|
+
opacity: motionProgress,
|
|
396
|
+
...viewportPadding,
|
|
397
|
+
paddingBottom: keyboardViewport.dockedKeyboard
|
|
398
|
+
? spacing.md + keyboardViewport.keyboardInset
|
|
399
|
+
: viewportPadding.paddingBottom,
|
|
400
|
+
}, 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) => {
|
|
401
|
+
const { width, height } = event.nativeEvent.layout;
|
|
402
|
+
const previous = originLayout.current;
|
|
403
|
+
originLayout.current = { width, height };
|
|
404
|
+
// Error copy, keyboard or rotation may change the destination after entry.
|
|
405
|
+
// Fade instead of returning through stale dimensions; a later open remeasures.
|
|
406
|
+
if (previous && (previous.width !== width || previous.height !== height))
|
|
407
|
+
setOriginTransform(null);
|
|
408
|
+
}, accessibilityLabel: [accessibleTitle, description].filter(Boolean).join(", "), accessibilityState: { busy }, accessibilityViewIsModal: true, importantForAccessibility: "yes", role: "dialog", style: [
|
|
409
|
+
{
|
|
410
|
+
alignSelf: "center",
|
|
411
|
+
backgroundColor: contentBackground,
|
|
412
|
+
borderColor: contentBorder,
|
|
413
|
+
borderRadius: tokens.radius[dialogRecipe.content.radius],
|
|
414
|
+
borderWidth: dialogRecipe.content.borderWidth,
|
|
415
|
+
...resolveNativeShadowElevation(surfaceShadow, designProfile !== undefined, 8),
|
|
416
|
+
gap: dialogRecipe.content.gap,
|
|
417
|
+
maxWidth: sizeRecipe.maxWidth,
|
|
418
|
+
maxHeight: "100%",
|
|
419
|
+
flexShrink: 1,
|
|
420
|
+
padding: sizeRecipe.padding,
|
|
421
|
+
shadowColor: surfaceShadow.color,
|
|
422
|
+
shadowOffset: { width: 0, height: surfaceShadow.offsetY },
|
|
423
|
+
shadowOpacity: surfaceShadow.opacity,
|
|
424
|
+
shadowRadius: surfaceShadow.radius,
|
|
425
|
+
width: "100%",
|
|
426
|
+
},
|
|
427
|
+
originTransform && !environment.reducedMotion ? { transform: [
|
|
428
|
+
{ translateX: motionProgress.interpolate({ inputRange: [0, 1], outputRange: [originTransform.translateX, 0] }) },
|
|
429
|
+
{ translateY: motionProgress.interpolate({ inputRange: [0, 1], outputRange: [originTransform.translateY, 0] }) },
|
|
430
|
+
{ scaleX: motionProgress.interpolate({ inputRange: [0, 1], outputRange: [originTransform.scaleX, 1] }) },
|
|
431
|
+
{ scaleY: motionProgress.interpolate({ inputRange: [0, 1], outputRange: [originTransform.scaleY, 1] }) },
|
|
432
|
+
] } : undefined,
|
|
433
|
+
contentStyle,
|
|
434
|
+
], children: [_jsxs(View, { style: { flexShrink: 1, gap: description ? spacing.xs : dialogRecipe.content.gap }, children: [_jsxs(View, { style: {
|
|
435
|
+
alignItems: "flex-start",
|
|
436
|
+
direction: environment.direction,
|
|
437
|
+
flexDirection: "row",
|
|
438
|
+
gap: spacing.sm,
|
|
439
|
+
}, 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, scrollEnabled: scrollEnabled, 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 }) })] })] }), overlayLayer ? _jsx(Animated.View, { pointerEvents: "none", accessibilityElementsHidden: true, importantForAccessibility: "no-hide-descendants", style: { bottom: 0, left: 0, opacity: motionProgress, position: "absolute", right: 0, top: 0 }, children: overlayLayer }) : null] }) }) }) }) }));
|
|
438
440
|
}
|
|
439
441
|
/** Contract session owns duplicate confirms, busy dismissal, error and settlement. */
|
|
440
442
|
export function AlertDialog({ open, defaultOpen, onOpenChange, request, size = alertDialogRecipe.defaults.size, returnFocusRef, onResult, contentStyle, onShow, ...modalProps }) {
|
|
@@ -678,66 +680,66 @@ export function AlertDialog({ open, defaultOpen, onOpenChange, request, size = a
|
|
|
678
680
|
},
|
|
679
681
|
], children: _jsx(Text, { style: { color: confirmContent }, variant: "label", children: request.confirmLabel }) }));
|
|
680
682
|
// Overlay content is not in the screen body: a bleeding rail inside must not reach for the screen gutter.
|
|
681
|
-
return (_jsx(ScreenBodyInsetContext.Provider, { value: 0, children: _jsx(InitialFocusProvider, { scope: focusScope, children: _jsx(Modal, { ...modalProps, animationType: "none", onDismiss: () => {
|
|
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
|
-
|
|
709
|
-
|
|
710
|
-
|
|
683
|
+
return (_jsx(ScreenBodyInsetContext.Provider, { value: 0, children: _jsx(BottomNavigationInsetContext.Provider, { value: 0, children: _jsx(InitialFocusProvider, { scope: focusScope, children: _jsx(Modal, { ...modalProps, animationType: "none", onDismiss: () => {
|
|
684
|
+
nativeShownRef.current = false;
|
|
685
|
+
const pending = pendingExitSessions.current[0];
|
|
686
|
+
if (pending)
|
|
687
|
+
completeExit(pending.session);
|
|
688
|
+
}, onRequestClose: () => cancel("back"), onShow: (event) => { focusLeastDestructive(event); setEntered(true); }, statusBarTranslucent: true, transparent: true, visible: nativeVisible, children: _jsx(ModalInteractionBoundary, { visible: visible, onTouchStart: focusScope.onTouchStart, children: _jsxs(Animated.View, { onLayout: (event) => keyboardViewport.setModalHeight(event.nativeEvent.layout.height), style: {
|
|
689
|
+
flex: 1,
|
|
690
|
+
justifyContent: "center",
|
|
691
|
+
opacity: motionProgress,
|
|
692
|
+
...viewportPadding,
|
|
693
|
+
paddingBottom: keyboardViewport.dockedKeyboard
|
|
694
|
+
? spacing.md + keyboardViewport.keyboardInset
|
|
695
|
+
: viewportPadding.paddingBottom,
|
|
696
|
+
}, children: [_jsx(Scrim, {}), _jsxs(View, { accessibilityLabel: `${request.title}, ${request.description}`, accessibilityState: { busy }, accessibilityViewIsModal: true, importantForAccessibility: "yes", role: "alertdialog", style: [
|
|
697
|
+
{
|
|
698
|
+
alignSelf: "center",
|
|
699
|
+
backgroundColor: contentBackground,
|
|
700
|
+
borderColor: contentBorder,
|
|
701
|
+
borderRadius: tokens.radius[alertDialogRecipe.content.radius],
|
|
702
|
+
borderWidth: alertDialogRecipe.content.borderWidth,
|
|
703
|
+
...resolveNativeShadowElevation(surfaceShadow, designProfile !== undefined, 8),
|
|
704
|
+
gap: alertDialogRecipe.content.gap,
|
|
705
|
+
maxWidth: sizeRecipe.maxWidth,
|
|
706
|
+
maxHeight: "100%",
|
|
707
|
+
flexShrink: 1,
|
|
708
|
+
padding: sizeRecipe.padding,
|
|
709
|
+
shadowColor: surfaceShadow.color,
|
|
710
|
+
shadowOffset: {
|
|
711
|
+
width: 0,
|
|
712
|
+
height: surfaceShadow.offsetY,
|
|
713
|
+
},
|
|
714
|
+
shadowOpacity: surfaceShadow.opacity,
|
|
715
|
+
shadowRadius: surfaceShadow.radius,
|
|
716
|
+
width: "100%",
|
|
711
717
|
},
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
opacity: busy ? 0.5 : pressed ? 0.86 : 1,
|
|
738
|
-
paddingHorizontal: spacing.md,
|
|
739
|
-
},
|
|
740
|
-
], children: _jsx(Text, { variant: "label", children: request.cancelLabel }) })) : null, stackActions ? null : confirmAction] })] })] }) }) }) }) }));
|
|
718
|
+
contentStyle,
|
|
719
|
+
], 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: {
|
|
720
|
+
direction: environment.direction,
|
|
721
|
+
flexDirection: stackActions ? "column" : "row",
|
|
722
|
+
gap: stackActions
|
|
723
|
+
? alertDialogRecipe.actions.stackedGap
|
|
724
|
+
: alertDialogRecipe.actions.gap,
|
|
725
|
+
}, 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 }) => [
|
|
726
|
+
minimumTargetStyle,
|
|
727
|
+
{
|
|
728
|
+
alignItems: "center",
|
|
729
|
+
// The cancel action is a quiet outline on the dialog surface. surfaceAlt
|
|
730
|
+
// turned it into a tinted slab wherever a product brands that token
|
|
731
|
+
// (BurnTok's pale blue, 2026-09-27), competing with the confirm fill.
|
|
732
|
+
backgroundColor: colors.bg,
|
|
733
|
+
borderColor: colors.border,
|
|
734
|
+
borderRadius: tokens.radius.md,
|
|
735
|
+
borderWidth: 1,
|
|
736
|
+
flex: stackActions ? undefined : 1,
|
|
737
|
+
justifyContent: "center",
|
|
738
|
+
minWidth: alertDialogRecipe.actions.minButtonWidth,
|
|
739
|
+
opacity: busy ? 0.5 : pressed ? 0.86 : 1,
|
|
740
|
+
paddingHorizontal: spacing.md,
|
|
741
|
+
},
|
|
742
|
+
], children: _jsx(Text, { variant: "label", children: request.cancelLabel }) })) : null, stackActions ? null : confirmAction] })] })] }) }) }) }) }) }));
|
|
741
743
|
}
|
|
742
744
|
/** Native Sheet applies policy before emitting a concrete dismissal reason. */
|
|
743
745
|
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,
|
|
@@ -997,94 +999,141 @@ keyboardAvoidance = true, scrollable = false, showClose, onShow, initialFocus =
|
|
|
997
999
|
const contentBackground = resolveColorReference(sheetRecipe.content.background, palette);
|
|
998
1000
|
const contentBorder = resolveColorReference(sheetRecipe.content.border, palette);
|
|
999
1001
|
// Overlay content is not in the screen body: a bleeding rail inside must not reach for the screen gutter.
|
|
1000
|
-
return (_jsx(ScreenBodyInsetContext.Provider, { value: 0, children: _jsx(InitialFocusProvider, { scope: focusScope, children: _jsx(Modal, { ...modalProps, animationType: "none", onDismiss: () => {
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1002
|
+
return (_jsx(ScreenBodyInsetContext.Provider, { value: 0, children: _jsx(BottomNavigationInsetContext.Provider, { value: 0, children: _jsx(InitialFocusProvider, { scope: focusScope, children: _jsx(Modal, { ...modalProps, animationType: "none", onDismiss: () => {
|
|
1003
|
+
nativeShownRef.current = false;
|
|
1004
|
+
const pending = pendingDismissals.current[0];
|
|
1005
|
+
if (pending)
|
|
1006
|
+
completeDismiss(pending.cycle);
|
|
1007
|
+
}, onRequestClose: () => requestClose("back"), onShow: (event) => {
|
|
1008
|
+
nativeShownRef.current = true;
|
|
1009
|
+
startEnter();
|
|
1010
|
+
setEntered(true);
|
|
1011
|
+
onShow?.(event);
|
|
1012
|
+
}, statusBarTranslucent: true, transparent: true, visible: nativeVisible, children: _jsx(ModalInteractionBoundary, { visible: visible, onTouchStart: focusScope.onTouchStart, children: _jsxs(Animated.View, { onLayout: (event) => setModalHeight(event.nativeEvent.layout.height), style: {
|
|
1013
|
+
alignItems: physicalPlacement === "right" ? "flex-end" : "flex-start",
|
|
1014
|
+
flex: 1,
|
|
1015
|
+
justifyContent: physicalPlacement === "bottom" ? "flex-end" : "flex-start",
|
|
1016
|
+
opacity: motionProgress,
|
|
1017
|
+
paddingTop: insets.top,
|
|
1018
|
+
paddingBottom: keyboardInset,
|
|
1019
|
+
}, children: [_jsx(Scrim, {}), policy.dismissible && policy.outsideDismiss ? (
|
|
1020
|
+
// Backdrop press closes every sheet by default (2026-10-17 user decision) through the same
|
|
1021
|
+
// requestClose path as the handle, so busy and dismissPolicy guards apply. Named by closeLabel.
|
|
1022
|
+
_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: [
|
|
1023
|
+
{
|
|
1024
|
+
backgroundColor: contentBackground,
|
|
1025
|
+
borderColor: contentBorder,
|
|
1026
|
+
borderRadius: tokens.radius[sheetRecipe.content.radius],
|
|
1027
|
+
borderBottomLeftRadius: physicalPlacement === "bottom" ? 0 : tokens.radius[sheetRecipe.content.radius],
|
|
1028
|
+
borderBottomRightRadius: physicalPlacement === "bottom" ? 0 : tokens.radius[sheetRecipe.content.radius],
|
|
1029
|
+
borderWidth: sheetRecipe.content.borderWidth,
|
|
1030
|
+
...resolveNativeShadowElevation(surfaceShadow, designProfile !== undefined, 8),
|
|
1031
|
+
gap: sheetRecipe.body.gap,
|
|
1032
|
+
// A fixed size ratio drives the height; `auto` lets the content decide
|
|
1033
|
+
// and the recipe's maxHeightRatio caps it.
|
|
1034
|
+
height: side
|
|
1035
|
+
? availableHeight
|
|
1036
|
+
: sizeRatio === null
|
|
1037
|
+
? undefined
|
|
1038
|
+
: Math.min(viewportHeight * sizeRatio, availableHeight),
|
|
1039
|
+
maxWidth: side ? 420 : undefined,
|
|
1040
|
+
// maxHeightRatio caps only `auto`. A fixed size is already bounded by availableHeight
|
|
1041
|
+
// (viewport − top inset − keyboard); capping it too stopped `full` at 90% (2026-10-06 follow-up).
|
|
1042
|
+
maxHeight: side || sizeRatio !== null
|
|
1043
|
+
? availableHeight
|
|
1044
|
+
: Math.min(availableHeight, viewportHeight * sheetRecipe.content.maxHeightRatio),
|
|
1045
|
+
paddingBottom: sheetRecipe.content.paddingBottom + bottomInset,
|
|
1046
|
+
paddingLeft: sheetRecipe.content.paddingHorizontal + insets.left,
|
|
1047
|
+
paddingRight: sheetRecipe.content.paddingHorizontal + insets.right,
|
|
1048
|
+
// The top safe area limits the viewport, rather than adding a second
|
|
1049
|
+
// notch-sized gap inside a sheet anchored to the bottom.
|
|
1050
|
+
paddingTop: sheetRecipe.content.paddingTop,
|
|
1051
|
+
shadowColor: surfaceShadow.color,
|
|
1052
|
+
shadowOffset: {
|
|
1053
|
+
width: 0,
|
|
1054
|
+
height: surfaceShadow.offsetY,
|
|
1055
|
+
},
|
|
1056
|
+
shadowOpacity: surfaceShadow.opacity,
|
|
1057
|
+
shadowRadius: surfaceShadow.radius,
|
|
1058
|
+
transform: environment.reducedMotion
|
|
1035
1059
|
? undefined
|
|
1036
|
-
:
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
? availableHeight
|
|
1042
|
-
: Math.min(availableHeight, viewportHeight * sheetRecipe.content.maxHeightRatio),
|
|
1043
|
-
paddingBottom: sheetRecipe.content.paddingBottom + bottomInset,
|
|
1044
|
-
paddingLeft: sheetRecipe.content.paddingHorizontal + insets.left,
|
|
1045
|
-
paddingRight: sheetRecipe.content.paddingHorizontal + insets.right,
|
|
1046
|
-
// The top safe area limits the viewport, rather than adding a second
|
|
1047
|
-
// notch-sized gap inside a sheet anchored to the bottom.
|
|
1048
|
-
paddingTop: sheetRecipe.content.paddingTop,
|
|
1049
|
-
shadowColor: surfaceShadow.color,
|
|
1050
|
-
shadowOffset: {
|
|
1051
|
-
width: 0,
|
|
1052
|
-
height: surfaceShadow.offsetY,
|
|
1060
|
+
: side
|
|
1061
|
+
? [{ translateX: translate }]
|
|
1062
|
+
// Two translateY entries compose: the enter/exit motion plus the live drag offset.
|
|
1063
|
+
: [{ translateY: translate }, { translateY: dragOffset }],
|
|
1064
|
+
width: side ? "88%" : "100%",
|
|
1053
1065
|
},
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
_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: {
|
|
1068
|
-
alignItems: "center",
|
|
1069
|
-
minHeight: sheetRecipe.header.minHeight,
|
|
1070
|
-
flexShrink: 0,
|
|
1071
|
-
direction: environment.direction,
|
|
1072
|
-
flexDirection: "row",
|
|
1073
|
-
// Without a description the × keeps its inline-end place (2026-10-17: no visible title).
|
|
1074
|
-
justifyContent: "flex-end",
|
|
1075
|
-
gap: spacing.sm,
|
|
1076
|
-
}, 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
|
|
1077
|
-
// The sheet's inline padding used to sit outside this clipping viewport, so ListRows cut their surface
|
|
1078
|
-
// bleed. The viewport now reaches the padding edge and carries the padding inside its content (Web's
|
|
1079
|
-
// body scroll box does the same); content keeps its position (2026-10-17 1.27.2 adoption).
|
|
1080
|
-
, {
|
|
1066
|
+
contentStyle,
|
|
1067
|
+
], 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 ? (
|
|
1068
|
+
// The handle doubles as a close control so switch and screen-reader users need no drag.
|
|
1069
|
+
_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: {
|
|
1070
|
+
alignItems: "center",
|
|
1071
|
+
minHeight: sheetRecipe.header.minHeight,
|
|
1072
|
+
flexShrink: 0,
|
|
1073
|
+
direction: environment.direction,
|
|
1074
|
+
flexDirection: "row",
|
|
1075
|
+
// Without a description the × keeps its inline-end place (2026-10-17: no visible title).
|
|
1076
|
+
justifyContent: "flex-end",
|
|
1077
|
+
gap: spacing.sm,
|
|
1078
|
+
}, 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
|
|
1081
1079
|
// The sheet's inline padding used to sit outside this clipping viewport, so ListRows cut their surface
|
|
1082
1080
|
// bleed. The viewport now reaches the padding edge and carries the padding inside its content (Web's
|
|
1083
1081
|
// body scroll box does the same); content keeps its position (2026-10-17 1.27.2 adoption).
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1082
|
+
, {
|
|
1083
|
+
// The sheet's inline padding used to sit outside this clipping viewport, so ListRows cut their surface
|
|
1084
|
+
// bleed. The viewport now reaches the padding edge and carries the padding inside its content (Web's
|
|
1085
|
+
// body scroll box does the same); content keeps its position (2026-10-17 1.27.2 adoption).
|
|
1086
|
+
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: {
|
|
1087
|
+
gap: sheetRecipe.footer.gap,
|
|
1088
|
+
flexShrink: 0,
|
|
1089
|
+
paddingTop: sheetRecipe.footer.paddingTop,
|
|
1090
|
+
}, children: footer })) : null] })] }) }) }) }) }) }));
|
|
1091
|
+
}
|
|
1092
|
+
/**
|
|
1093
|
+
* Edge-to-edge presentation (Dialog companion): a full-screen Modal with the status bar hidden and rotation allowed,
|
|
1094
|
+
* no card, scrim or header. It joins the overlay stack (screens below keep a pending entry focus until it has torn
|
|
1095
|
+
* down), dismisses on back with a reason, and returns accessibility focus after the native dismissal.
|
|
1096
|
+
*/
|
|
1097
|
+
export function ImmersiveOverlay({ open, onOpenChange, label, exitLabel, children, tools, status, returnFocusRef, onShow }) {
|
|
1098
|
+
const { palette, environment } = useHjmNativeTheme();
|
|
1099
|
+
const insets = useHjmNativeSafeAreaInsets();
|
|
1100
|
+
const recipe = dialogRecipe.immersive;
|
|
1101
|
+
const [layered, setLayered] = useState(open);
|
|
1102
|
+
const [entered, setEntered] = useState(false);
|
|
1103
|
+
const wasOpen = useRef(open);
|
|
1104
|
+
const shown = useRef(false);
|
|
1105
|
+
const pendingDismiss = useRef(null);
|
|
1106
|
+
const focusScope = useInitialFocusScope({ mode: "none", active: open && entered, layer: useOverlayLayer(layered) });
|
|
1107
|
+
useEffect(() => {
|
|
1108
|
+
if (open) {
|
|
1109
|
+
wasOpen.current = true;
|
|
1110
|
+
pendingDismiss.current = null;
|
|
1111
|
+
setLayered(true);
|
|
1112
|
+
return undefined;
|
|
1113
|
+
}
|
|
1114
|
+
setEntered(false);
|
|
1115
|
+
if (!wasOpen.current)
|
|
1116
|
+
return undefined;
|
|
1117
|
+
wasOpen.current = false;
|
|
1118
|
+
// Leave the stack and return focus once the native window is gone, the point Dialog and Sheet use.
|
|
1119
|
+
const finish = () => { pendingDismiss.current = null; setLayered(false); focusNativeTarget(returnFocusRef); };
|
|
1120
|
+
if (shouldAwaitNativeModalDismiss(shown.current)) {
|
|
1121
|
+
pendingDismiss.current = finish;
|
|
1122
|
+
return undefined;
|
|
1123
|
+
}
|
|
1124
|
+
const task = scheduleAfterNativeModalTeardown(finish);
|
|
1125
|
+
return () => task.cancel();
|
|
1126
|
+
}, [open, returnFocusRef]);
|
|
1127
|
+
const background = resolveColorReference(recipe.background, palette);
|
|
1128
|
+
// The stage is not in a screen body: a bleeding rail inside must not reach for the screen gutter.
|
|
1129
|
+
return (_jsx(ScreenBodyInsetContext.Provider, { value: 0, children: _jsx(InitialFocusProvider, { scope: focusScope, children: _jsxs(Modal, { visible: open,
|
|
1130
|
+
// Animation "none": teardown completion is scheduled after removal, which assumes no built-in exit animation.
|
|
1131
|
+
animationType: "none", presentationStyle: "fullScreen", supportedOrientations: [...recipe.orientations], statusBarTranslucent: true, onRequestClose: () => onOpenChange(false, { reason: "back" }), onShow: (event) => { shown.current = true; setEntered(true); onShow?.(event); }, onDismiss: () => { shown.current = false; pendingDismiss.current?.(); }, children: [_jsx(StatusBar, { hidden: recipe.statusBarHidden }), _jsxs(View, { onTouchStart: focusScope.onTouchStart, accessibilityViewIsModal: true, accessibilityLabel: label, role: "dialog", style: { backgroundColor: background, direction: environment.direction, flex: 1 }, children: [_jsx(View, { style: { flex: 1, overflow: "hidden" }, children: children }), _jsxs(View, { style: {
|
|
1132
|
+
gap: recipe.controls.gap,
|
|
1133
|
+
paddingTop: recipe.controls.padding,
|
|
1134
|
+
paddingBottom: Math.max(recipe.controls.padding, insets.bottom ?? 0),
|
|
1135
|
+
paddingLeft: Math.max(recipe.controls.padding, insets.left ?? 0),
|
|
1136
|
+
paddingRight: Math.max(recipe.controls.padding, insets.right ?? 0),
|
|
1137
|
+
}, children: [status, _jsxs(View, { style: { alignItems: "center", flexDirection: "row", gap: recipe.controls.actionGap }, children: [tools, _jsx(Button, { size: "large", growWithContent: true, layoutStyle: { flex: 1 }, onPress: () => onOpenChange(false, { reason: "exit-action" }), children: exitLabel })] })] })] })] }) }) }));
|
|
1089
1138
|
}
|
|
1090
1139
|
//# sourceMappingURL=overlays.js.map
|