@symbiote-native/navigation 1.0.0 → 2.0.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/README.md +159 -0
- package/package.json +14 -12
- package/build/angular/drawer/index.d.ts +0 -76
- package/build/angular/drawer/index.js +0 -508
- package/build/angular/drawer-screen.directive.d.ts +0 -14
- package/build/angular/drawer-screen.directive.js +0 -87
- package/build/angular/index.d.ts +0 -23
- package/build/angular/index.js +0 -22
- package/build/angular/injectors/index.d.ts +0 -13
- package/build/angular/injectors/index.js +0 -8
- package/build/angular/injectors/inject-drawer-navigation.d.ts +0 -4
- package/build/angular/injectors/inject-drawer-navigation.js +0 -12
- package/build/angular/injectors/inject-focus-effect.d.ts +0 -2
- package/build/angular/injectors/inject-focus-effect.js +0 -29
- package/build/angular/injectors/inject-is-focused.d.ts +0 -2
- package/build/angular/injectors/inject-is-focused.js +0 -20
- package/build/angular/injectors/inject-navigation-state.d.ts +0 -3
- package/build/angular/injectors/inject-navigation-state.js +0 -35
- package/build/angular/injectors/inject-navigation.d.ts +0 -7
- package/build/angular/injectors/inject-navigation.js +0 -16
- package/build/angular/injectors/inject-route.d.ts +0 -3
- package/build/angular/injectors/inject-route.js +0 -20
- package/build/angular/injectors/inject-stack-navigation.d.ts +0 -4
- package/build/angular/injectors/inject-stack-navigation.js +0 -15
- package/build/angular/injectors/inject-tab-navigation.d.ts +0 -4
- package/build/angular/injectors/inject-tab-navigation.js +0 -11
- package/build/angular/linking.d.ts +0 -3
- package/build/angular/linking.js +0 -43
- package/build/angular/navigation-context.service.d.ts +0 -13
- package/build/angular/navigation-context.service.js +0 -105
- package/build/angular/navigation-scope.directive.d.ts +0 -10
- package/build/angular/navigation-scope.directive.js +0 -90
- package/build/angular/screen.directive.d.ts +0 -22
- package/build/angular/screen.directive.js +0 -90
- package/build/angular/search-bar-ref.directive.d.ts +0 -10
- package/build/angular/search-bar-ref.directive.js +0 -88
- package/build/angular/stack/index.d.ts +0 -55
- package/build/angular/stack/index.js +0 -386
- package/build/angular/tab-screen.directive.d.ts +0 -14
- package/build/angular/tab-screen.directive.js +0 -87
- package/build/angular/tabs/index.d.ts +0 -42
- package/build/angular/tabs/index.js +0 -263
- package/build/core/constants.d.ts +0 -27
- package/build/core/constants.js +0 -61
- package/build/core/drawer-options/index.d.ts +0 -64
- package/build/core/drawer-options/index.js +0 -200
- package/build/core/drawer-router-state/index.d.ts +0 -21
- package/build/core/drawer-router-state/index.js +0 -40
- package/build/core/guards.d.ts +0 -2
- package/build/core/guards.js +0 -22
- package/build/core/header-bar-buttons.d.ts +0 -9
- package/build/core/header-bar-buttons.js +0 -163
- package/build/core/index.d.ts +0 -29
- package/build/core/index.js +0 -32
- package/build/core/linking-config/index.d.ts +0 -13
- package/build/core/linking-config/index.js +0 -171
- package/build/core/navigation-events/index.d.ts +0 -16
- package/build/core/navigation-events/index.js +0 -57
- package/build/core/navigator-handles/index.d.ts +0 -30
- package/build/core/navigator-handles/index.js +0 -15
- package/build/core/navigator-props.d.ts +0 -216
- package/build/core/navigator-props.js +0 -7
- package/build/core/navigator-state/index.d.ts +0 -36
- package/build/core/navigator-state/index.js +0 -81
- package/build/core/render-drawer.d.ts +0 -13
- package/build/core/render-drawer.js +0 -88
- package/build/core/render-stack.d.ts +0 -35
- package/build/core/render-stack.js +0 -294
- package/build/core/render-tabs.d.ts +0 -20
- package/build/core/render-tabs.js +0 -97
- package/build/core/screen-options.d.ts +0 -4
- package/build/core/screen-options.js +0 -133
- package/build/core/search-bar-commands.d.ts +0 -10
- package/build/core/search-bar-commands.js +0 -42
- package/build/core/state-persistence/index.d.ts +0 -3
- package/build/core/state-persistence/index.js +0 -23
- package/build/core/tab-options.d.ts +0 -13
- package/build/core/tab-options.js +0 -10
- package/build/core/tab-router-state/index.d.ts +0 -19
- package/build/core/tab-router-state/index.js +0 -51
- package/build/react/collect-registry.d.ts +0 -4
- package/build/react/collect-registry.js +0 -15
- package/build/react/drawer/index.d.ts +0 -29
- package/build/react/drawer/index.js +0 -277
- package/build/react/drawer-screen.d.ts +0 -14
- package/build/react/drawer-screen.js +0 -8
- package/build/react/hooks/index.d.ts +0 -12
- package/build/react/hooks/index.js +0 -8
- package/build/react/hooks/use-drawer-navigation.d.ts +0 -4
- package/build/react/hooks/use-drawer-navigation.js +0 -12
- package/build/react/hooks/use-focus-effect.d.ts +0 -2
- package/build/react/hooks/use-focus-effect.js +0 -29
- package/build/react/hooks/use-is-focused.d.ts +0 -1
- package/build/react/hooks/use-is-focused.js +0 -22
- package/build/react/hooks/use-navigation-state/index.d.ts +0 -2
- package/build/react/hooks/use-navigation-state/index.js +0 -39
- package/build/react/hooks/use-navigation.d.ts +0 -7
- package/build/react/hooks/use-navigation.js +0 -16
- package/build/react/hooks/use-route.d.ts +0 -2
- package/build/react/hooks/use-route.js +0 -8
- package/build/react/hooks/use-stack-navigation.d.ts +0 -4
- package/build/react/hooks/use-stack-navigation.js +0 -14
- package/build/react/hooks/use-tab-navigation.d.ts +0 -4
- package/build/react/hooks/use-tab-navigation.js +0 -12
- package/build/react/index.d.ts +0 -24
- package/build/react/index.js +0 -21
- package/build/react/linking.d.ts +0 -3
- package/build/react/linking.js +0 -47
- package/build/react/navigation-context.d.ts +0 -10
- package/build/react/navigation-context.js +0 -22
- package/build/react/screen.d.ts +0 -22
- package/build/react/screen.js +0 -7
- package/build/react/stack/index.d.ts +0 -13
- package/build/react/stack/index.js +0 -202
- package/build/react/tab-screen.d.ts +0 -15
- package/build/react/tab-screen.js +0 -8
- package/build/react/tabs/index.d.ts +0 -12
- package/build/react/tabs/index.js +0 -116
- package/build/register.d.ts +0 -7
- package/build/register.js +0 -58
- package/build/vue/composables/index.d.ts +0 -12
- package/build/vue/composables/index.js +0 -8
- package/build/vue/composables/use-drawer-navigation.d.ts +0 -5
- package/build/vue/composables/use-drawer-navigation.js +0 -15
- package/build/vue/composables/use-focus-effect.d.ts +0 -1
- package/build/vue/composables/use-focus-effect.js +0 -31
- package/build/vue/composables/use-is-focused.d.ts +0 -2
- package/build/vue/composables/use-is-focused.js +0 -28
- package/build/vue/composables/use-navigation-state.d.ts +0 -3
- package/build/vue/composables/use-navigation-state.js +0 -29
- package/build/vue/composables/use-navigation.d.ts +0 -8
- package/build/vue/composables/use-navigation.js +0 -13
- package/build/vue/composables/use-route.d.ts +0 -3
- package/build/vue/composables/use-route.js +0 -9
- package/build/vue/composables/use-stack-navigation.d.ts +0 -5
- package/build/vue/composables/use-stack-navigation.js +0 -18
- package/build/vue/composables/use-tab-navigation.d.ts +0 -5
- package/build/vue/composables/use-tab-navigation.js +0 -15
- package/build/vue/drawer/index.d.ts +0 -19
- package/build/vue/drawer/index.js +0 -390
- package/build/vue/drawer-screen.d.ts +0 -14
- package/build/vue/drawer-screen.js +0 -12
- package/build/vue/index.d.ts +0 -21
- package/build/vue/index.js +0 -19
- package/build/vue/linking.d.ts +0 -3
- package/build/vue/linking.js +0 -57
- package/build/vue/navigation-context.d.ts +0 -16
- package/build/vue/navigation-context.js +0 -65
- package/build/vue/screen.d.ts +0 -21
- package/build/vue/screen.js +0 -12
- package/build/vue/stack/index.d.ts +0 -9
- package/build/vue/stack/index.js +0 -279
- package/build/vue/tab-screen.d.ts +0 -14
- package/build/vue/tab-screen.js +0 -11
- package/build/vue/tabs/index.d.ts +0 -10
- package/build/vue/tabs/index.js +0 -209
- package/build-ngc/angular/drawer.d.ts +0 -79
- package/build-ngc/angular/drawer.js +0 -475
- package/build-ngc/angular/drawer.js.map +0 -1
- package/build-ngc/angular/stack.d.ts +0 -58
- package/build-ngc/angular/stack.js +0 -402
- package/build-ngc/angular/stack.js.map +0 -1
- package/build-ngc/angular/tabs.d.ts +0 -45
- package/build-ngc/angular/tabs.js +0 -231
- package/build-ngc/angular/tabs.js.map +0 -1
- package/build-ngc/core/drawer-options.d.ts +0 -64
- package/build-ngc/core/drawer-options.js +0 -201
- package/build-ngc/core/drawer-options.js.map +0 -1
- package/build-ngc/core/drawer-router-state.d.ts +0 -21
- package/build-ngc/core/drawer-router-state.js +0 -41
- package/build-ngc/core/drawer-router-state.js.map +0 -1
- package/build-ngc/core/href.d.ts +0 -16
- package/build-ngc/core/href.js +0 -55
- package/build-ngc/core/href.js.map +0 -1
- package/build-ngc/core/linking-config.d.ts +0 -13
- package/build-ngc/core/linking-config.js +0 -172
- package/build-ngc/core/linking-config.js.map +0 -1
- package/build-ngc/core/navigation-events.d.ts +0 -16
- package/build-ngc/core/navigation-events.js +0 -58
- package/build-ngc/core/navigation-events.js.map +0 -1
- package/build-ngc/core/navigator-handles.d.ts +0 -30
- package/build-ngc/core/navigator-handles.js +0 -16
- package/build-ngc/core/navigator-handles.js.map +0 -1
- package/build-ngc/core/navigator-state.d.ts +0 -36
- package/build-ngc/core/navigator-state.js +0 -82
- package/build-ngc/core/navigator-state.js.map +0 -1
- package/build-ngc/core/search-params.d.ts +0 -9
- package/build-ngc/core/search-params.js +0 -60
- package/build-ngc/core/search-params.js.map +0 -1
- package/build-ngc/core/state-persistence.d.ts +0 -3
- package/build-ngc/core/state-persistence.js +0 -24
- package/build-ngc/core/state-persistence.js.map +0 -1
- package/build-ngc/core/tab-router-state.d.ts +0 -19
- package/build-ngc/core/tab-router-state.js +0 -52
- package/build-ngc/core/tab-router-state.js.map +0 -1
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
// Drawer: the render half (framework-agnostic). Like render-stack.ts's screen content, both the
|
|
2
|
-
// drawer panel's content (renderDrawerContent) and the wrapped screen content are arbitrary
|
|
3
|
-
// framework subtrees the adapter owns - so, mirroring render-modal.ts's container-slot precedent,
|
|
4
|
-
// renderDrawer hands back one root Descriptor whose structural children are EMPTY placeholder
|
|
5
|
-
// nodes (`content` / `overlay?` / `panel`) that the adapter fills with real framework children.
|
|
6
|
-
// Paint order (and therefore which slot is present) is drawerType-dependent, so `drawerChildOrder`
|
|
7
|
-
// is exported alongside renderDrawer as the single source of truth the adapter zips its own
|
|
8
|
-
// children against - no adapter re-derives the ordering itself.
|
|
9
|
-
//
|
|
10
|
-
// All positioning here is STATIC (position/top/bottom/side/width) - the animated slide transform
|
|
11
|
-
// and overlay opacity are NOT baked into this Descriptor. They are computed once per drawerType
|
|
12
|
-
// via drawer-options.ts's resolveDrawerGeometry() and then driven per-frame by the adapter's own
|
|
13
|
-
// Animated value (Animated.View), because "how to animate a style" is a framework-lifecycle
|
|
14
|
-
// concern (React: Animated.View; a future Vue adapter: a reactive style binding) - see
|
|
15
|
-
// CLAUDE.md <adapters_stay_thin>.
|
|
16
|
-
import { el } from '@symbiote-native/components';
|
|
17
|
-
import { isDrawerOverlayVisible, resolveDrawerPosition, resolveDrawerType, resolveDrawerWidth, } from './drawer-options/index.js';
|
|
18
|
-
// front: content, overlay (dims it), panel (paints on top, last-sibling-on-top).
|
|
19
|
-
// back: panel (stationary, behind), content (slides away to reveal it) - no overlay. Both are
|
|
20
|
-
// absolutely positioned, so sibling order here only decides z-stacking, not screen side.
|
|
21
|
-
// slide: panel, overlay, content (both panel and content animate; content stays visually on top).
|
|
22
|
-
// permanent: NOT absolutely positioned (an ordinary flexDirection:'row' sidebar), so sibling
|
|
23
|
-
// order IS the left-to-right screen position - it must follow drawerPosition.
|
|
24
|
-
export function drawerChildOrder(options) {
|
|
25
|
-
const type = resolveDrawerType(options);
|
|
26
|
-
switch (type) {
|
|
27
|
-
case 'back':
|
|
28
|
-
return ['panel', 'content'];
|
|
29
|
-
case 'slide':
|
|
30
|
-
return ['panel', 'overlay', 'content'];
|
|
31
|
-
case 'permanent':
|
|
32
|
-
return resolveDrawerPosition(options) === 'left'
|
|
33
|
-
? ['panel', 'content']
|
|
34
|
-
: ['content', 'panel'];
|
|
35
|
-
case 'front':
|
|
36
|
-
default:
|
|
37
|
-
return ['content', 'overlay', 'panel'];
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
const ROOT_STYLE = { flex: 1 };
|
|
41
|
-
const CONTENT_STYLE = { flex: 1 };
|
|
42
|
-
// Fabric paints later siblings on top; position:absolute + inset:0 is enough for the overlay
|
|
43
|
-
// (its own sibling order, not zIndex, decides whether it sits above the content).
|
|
44
|
-
const OVERLAY_BASE_STYLE = {
|
|
45
|
-
position: 'absolute',
|
|
46
|
-
top: 0,
|
|
47
|
-
left: 0,
|
|
48
|
-
right: 0,
|
|
49
|
-
bottom: 0,
|
|
50
|
-
};
|
|
51
|
-
const DRAWER_PANEL_DEFAULT_BACKGROUND_COLOR = 'white';
|
|
52
|
-
function panelBaseStyle(options) {
|
|
53
|
-
const type = resolveDrawerType(options);
|
|
54
|
-
const width = resolveDrawerWidth(options);
|
|
55
|
-
const side = resolveDrawerPosition(options);
|
|
56
|
-
if (type === 'permanent') {
|
|
57
|
-
// A sidebar: an ordinary flex child, no absolute positioning, no translate needed.
|
|
58
|
-
return { width, backgroundColor: DRAWER_PANEL_DEFAULT_BACKGROUND_COLOR };
|
|
59
|
-
}
|
|
60
|
-
return {
|
|
61
|
-
position: 'absolute',
|
|
62
|
-
top: 0,
|
|
63
|
-
bottom: 0,
|
|
64
|
-
width,
|
|
65
|
-
backgroundColor: DRAWER_PANEL_DEFAULT_BACKGROUND_COLOR,
|
|
66
|
-
[side]: 0,
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
export function renderDrawer(view, options) {
|
|
70
|
-
const type = resolveDrawerType(options);
|
|
71
|
-
const content = el('symbiote-view', { ...view.contentPassthrough, style: CONTENT_STYLE }, []);
|
|
72
|
-
const panel = el('symbiote-view', { ...view.panelPassthrough, style: [panelBaseStyle(options), view.drawerStyle] }, []);
|
|
73
|
-
const slots = { content, panel };
|
|
74
|
-
if (isDrawerOverlayVisible(options)) {
|
|
75
|
-
slots.overlay = el('symbiote-view', {
|
|
76
|
-
...view.overlayPassthrough,
|
|
77
|
-
style: [OVERLAY_BASE_STYLE, { backgroundColor: view.overlayColor }],
|
|
78
|
-
}, []);
|
|
79
|
-
}
|
|
80
|
-
const children = drawerChildOrder(options).reduce((acc, slot) => {
|
|
81
|
-
const descriptor = slots[slot];
|
|
82
|
-
if (descriptor !== undefined)
|
|
83
|
-
acc.push(descriptor);
|
|
84
|
-
return acc;
|
|
85
|
-
}, []);
|
|
86
|
-
const rootStyle = type === 'permanent' ? { ...ROOT_STYLE, flexDirection: 'row' } : ROOT_STYLE;
|
|
87
|
-
return el('symbiote-view', { style: rootStyle }, children);
|
|
88
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import type { IDescriptor } from '@symbiote-native/components';
|
|
2
|
-
import type { ISymbioteEvent } from '@symbiote-native/engine';
|
|
3
|
-
import type { IHeaderConfigViewProps, INavigatorPlatform, IScreenOptions, IScreenViewProps, ISearchBarOptions, ISearchBarViewProps, IStackPresentation, IStackViewProps } from './navigator-props';
|
|
4
|
-
export declare function resolveScreenViewName(stackPresentation: IStackPresentation | undefined, isAndroid: boolean): string;
|
|
5
|
-
export declare function resolveScreenContentWrapperStyle(stackPresentation: IStackPresentation | undefined, headerHidden: boolean, headerTranslucent: boolean | undefined, isAndroid: boolean): Record<string, unknown>;
|
|
6
|
-
export declare function isHeaderInModal(stackPresentation: IStackPresentation | undefined, headerHidden: boolean, isAndroid: boolean): boolean;
|
|
7
|
-
export declare function resolveHeaderInModalStackStyle(): Record<string, unknown>;
|
|
8
|
-
export declare function resolveHeaderInModalScreenStyle(): Record<string, unknown>;
|
|
9
|
-
export declare function resolveScreenProps(view: IScreenViewProps): Record<string, unknown>;
|
|
10
|
-
export declare function resolveStackProps(view: IStackViewProps): Record<string, unknown>;
|
|
11
|
-
export declare function renderHeaderConfig(view: IHeaderConfigViewProps, searchBarProps?: Record<string, unknown>): IDescriptor;
|
|
12
|
-
export declare function resolveSearchBarProps(view: ISearchBarViewProps): Record<string, unknown>;
|
|
13
|
-
export declare function buildSearchBarPassthrough(options: ISearchBarOptions, log?: (message: string) => void): Record<string, (event: ISymbioteEvent) => void>;
|
|
14
|
-
export type IScreenRenderPlanInput = {
|
|
15
|
-
screenId: string;
|
|
16
|
-
index: number;
|
|
17
|
-
routeCount: number;
|
|
18
|
-
options: IScreenOptions | undefined;
|
|
19
|
-
platform: INavigatorPlatform;
|
|
20
|
-
isAndroid: boolean;
|
|
21
|
-
screenPassthrough: Record<string, unknown>;
|
|
22
|
-
searchBarPassthrough?: Record<string, unknown>;
|
|
23
|
-
};
|
|
24
|
-
export type IScreenRenderPlan = {
|
|
25
|
-
activityState: number;
|
|
26
|
-
screenViewName: string;
|
|
27
|
-
screenProps: Record<string, unknown>;
|
|
28
|
-
headerConfig: IDescriptor;
|
|
29
|
-
searchBarProps: Record<string, unknown> | undefined;
|
|
30
|
-
contentWrapperProps: Record<string, unknown>;
|
|
31
|
-
inModal: boolean;
|
|
32
|
-
innerStackStyle: Record<string, unknown>;
|
|
33
|
-
innerScreenStyle: Record<string, unknown>;
|
|
34
|
-
};
|
|
35
|
-
export declare function resolveScreenRenderPlan(input: IScreenRenderPlanInput): IScreenRenderPlan;
|
|
@@ -1,294 +0,0 @@
|
|
|
1
|
-
// Navigator: framework-agnostic render half. A screen's content is an arbitrary framework
|
|
2
|
-
// subtree the adapter owns (unlike the slider's one native leaf), so these resolvers hand back
|
|
3
|
-
// plain props objects for the RNSScreen/RNSScreenStack leaves the adapter builds itself with real
|
|
4
|
-
// children, and one full Descriptor for the header config. The header config Descriptor never
|
|
5
|
-
// takes framework children (headerLeftBarButtonItems/headerRightBarButtonItems are native config
|
|
6
|
-
// arrays from screen-options.ts, not elements), but can nest one RNSSearchBar leaf wrapped in an
|
|
7
|
-
// RNSScreenStackHeaderSubview(type: 'searchBar'), matching how react-native-screens' own
|
|
8
|
-
// ScreenStackHeaderSearchBarView mounts it. RNSScreenStackHeaderConfig.mm's mountChildComponentView
|
|
9
|
-
// rejects a bare RNSSearchBar child natively ("only accepts children of type
|
|
10
|
-
// ScreenStackHeaderSubview") and reads the search bar back out via `subview.subviews[0]`.
|
|
11
|
-
import { el } from '@symbiote-native/components';
|
|
12
|
-
import { HEADER_ON_PRESS_BAR_BUTTON_ITEM, HEADER_ON_PRESS_BAR_BUTTON_MENU_ITEM, RNS_MODAL_SCREEN_VIEW_NAME, RNS_SCREEN_STACK_HEADER_CONFIG_VIEW_NAME, RNS_SCREEN_STACK_HEADER_SUBVIEW_VIEW_NAME, RNS_SCREEN_VIEW_NAME, RNS_SEARCH_BAR_VIEW_NAME, SEARCH_BAR_ON_BLUR, SEARCH_BAR_ON_CANCEL_BUTTON_PRESS, SEARCH_BAR_ON_CHANGE_TEXT, SEARCH_BAR_ON_CLOSE, SEARCH_BAR_ON_FOCUS, SEARCH_BAR_ON_OPEN, SEARCH_BAR_ON_SEARCH_BUTTON_PRESS, STACK_DEFAULT_ANIMATION, STACK_DEFAULT_PRESENTATION, } from './constants.js';
|
|
13
|
-
import { computeActivityState } from './navigator-state/index.js';
|
|
14
|
-
import { resolveHeaderConfigView, resolveScreenView, resolveSearchBarView } from './screen-options.js';
|
|
15
|
-
// Mirrors react-native-screens' ScreenStackItem.tsx (getPositioningStyle): a formSheet's content
|
|
16
|
-
// wrapper does not get `flex: 1`. RNSScreen.mm's updateBounds pushes the sheet's live native size
|
|
17
|
-
// into Fabric's shadow state on every frame of a detent drag, and a `bottom: 0`-pinned wrapper
|
|
18
|
-
// forces React to set a strict frame on every update, causing the flicker PR #1870 fixed by
|
|
19
|
-
// switching to `absoluteWithNoBottom` (RNSScreen.mm: "we do not set bottom: 0 in JS for wrapper of
|
|
20
|
-
// the screen content, causing React to not set strict frame every time the sheet size is updated").
|
|
21
|
-
// Tradeoff: content shorter than the sheet's current detent leaves a gap below it.
|
|
22
|
-
// RNSScreenContentWrapper.mm's coerceChildScrollViewComponentSizeToSize fixes that natively, but
|
|
23
|
-
// only for a ScrollView child, which it resizes directly to the sheet's real frame bypassing
|
|
24
|
-
// Yoga/flex, not something reachable from a style choice here.
|
|
25
|
-
const SCREEN_CONTENT_WRAPPER_FLEX_STYLE = { flex: 1 };
|
|
26
|
-
const SHEET_CONTENT_WRAPPER_STYLE = {
|
|
27
|
-
position: 'absolute',
|
|
28
|
-
top: 0,
|
|
29
|
-
start: 0,
|
|
30
|
-
end: 0,
|
|
31
|
-
bottom: undefined,
|
|
32
|
-
};
|
|
33
|
-
// Android's native header (react-native-screens' CustomToolbar in a CoordinatorLayout alongside
|
|
34
|
-
// an AppBarLayout) auto-offsets screen content below the header via a Material
|
|
35
|
-
// `AppBarLayout.ScrollingViewBehavior` (ScreenStackFragment.kt's setToolbarTranslucent: `behavior
|
|
36
|
-
// = if (translucent) null else ScrollingViewBehavior()`). A plain (non-translucent) header gets
|
|
37
|
-
// this offset for free and needs no JS compensation. `headerTranslucent: true` removes that
|
|
38
|
-
// behavior: react-native-screens' contract for translucent is "background goes away AND content
|
|
39
|
-
// is no longer auto-pushed below it", meant for a screen painting under a see-through header. An
|
|
40
|
-
// app that sets headerTranslucent just for a custom (still opaque) header look, while relying on
|
|
41
|
-
// its own SafeAreaView for safe-area content, loses the free offset and gets clipped:
|
|
42
|
-
// SafeAreaView's Android inset only reflects WindowInsetsCompat's systemBars() (status
|
|
43
|
-
// bar/cutout), with no awareness of the sibling Toolbar, so content paints its first 56dp under
|
|
44
|
-
// the header. Verified empirically (2026-07-10) via `adb shell uiautomator dump`: a
|
|
45
|
-
// translucent-header screen's content measured starting at y=0 (see the
|
|
46
|
-
// navigation-header-content-offset skill for the full incident). iOS's UINavigationController
|
|
47
|
-
// already lays out below its real nav bar regardless of translucency, so this compensation is
|
|
48
|
-
// Android-only and applies only when translucent (a non-translucent header already gets the
|
|
49
|
-
// native offset; padding there would double it).
|
|
50
|
-
const ANDROID_HEADER_TOOLBAR_HEIGHT = 56;
|
|
51
|
-
// react-native-screens' own Screen.tsx picks a different Fabric component for a modally-presented
|
|
52
|
-
// screen ('RNSModalScreen' instead of plain 'RNSScreen') because "there is a need for different
|
|
53
|
-
// shadow nodes" (its own comment). RNSScreen.mm's updateLayoutMetrics only skips applying Yoga's
|
|
54
|
-
// computed frame for `[self isKindOfClass:RNSModalScreen.class]` ("the available space is most
|
|
55
|
-
// likely restricted & differs from what Yoga resolves during first layout - we want to rely on
|
|
56
|
-
// native layout here"). Emitting plain 'RNSScreen' for a formSheet/modal means that check never
|
|
57
|
-
// matches, so Yoga's frame (computed as an ordinary push-stack sibling) applies uncorrected,
|
|
58
|
-
// shifting the modal down by one full screen height (see register.ts's incident note). Mirrors
|
|
59
|
-
// Screen.tsx's `shouldUseModalScreenComponent` exactly, including the iOS-only scope (Android
|
|
60
|
-
// sizes modals natively regardless of Fabric view name).
|
|
61
|
-
export function resolveScreenViewName(stackPresentation, isAndroid) {
|
|
62
|
-
if (isAndroid)
|
|
63
|
-
return RNS_SCREEN_VIEW_NAME;
|
|
64
|
-
const isPlainPresentation = stackPresentation === undefined ||
|
|
65
|
-
stackPresentation === 'push' ||
|
|
66
|
-
stackPresentation === 'containedModal' ||
|
|
67
|
-
stackPresentation === 'containedTransparentModal';
|
|
68
|
-
return isPlainPresentation ? RNS_SCREEN_VIEW_NAME : RNS_MODAL_SCREEN_VIEW_NAME;
|
|
69
|
-
}
|
|
70
|
-
export function resolveScreenContentWrapperStyle(stackPresentation, headerHidden, headerTranslucent, isAndroid) {
|
|
71
|
-
let baseStyles;
|
|
72
|
-
if (stackPresentation !== 'formSheet') {
|
|
73
|
-
baseStyles = SCREEN_CONTENT_WRAPPER_FLEX_STYLE;
|
|
74
|
-
}
|
|
75
|
-
else if (isAndroid) {
|
|
76
|
-
baseStyles = {
|
|
77
|
-
...SHEET_CONTENT_WRAPPER_STYLE,
|
|
78
|
-
bottom: 0,
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
baseStyles = SHEET_CONTENT_WRAPPER_STYLE;
|
|
83
|
-
}
|
|
84
|
-
// paddingTop compensates the Android-only ScrollingViewBehavior removal above; iOS never needs
|
|
85
|
-
// it, so the `!isAndroid` check must stay.
|
|
86
|
-
if (!isAndroid || headerHidden || !headerTranslucent)
|
|
87
|
-
return baseStyles;
|
|
88
|
-
return { ...baseStyles, paddingTop: ANDROID_HEADER_TOOLBAR_HEIGHT };
|
|
89
|
-
}
|
|
90
|
-
// A modally-presented screen (anything but 'push') has no UINavigationController on iOS, so
|
|
91
|
-
// RNSScreenStackHeaderConfig has nothing to attach a header bar to. react-native-screens' own
|
|
92
|
-
// ScreenStackItem.tsx (isHeaderInModal) works around this by nesting header + content inside a
|
|
93
|
-
// second, inner RNSScreenStack/RNSScreen pair purely to host the native nav bar. This is a UIKit
|
|
94
|
-
// constraint, not a React-renderer one, so it applies identically here. Android has no such
|
|
95
|
-
// requirement (its header draws without a nav-controller host).
|
|
96
|
-
export function isHeaderInModal(stackPresentation, headerHidden, isAndroid) {
|
|
97
|
-
if (isAndroid)
|
|
98
|
-
return false;
|
|
99
|
-
return (stackPresentation ?? STACK_DEFAULT_PRESENTATION) !== 'push' && !headerHidden;
|
|
100
|
-
}
|
|
101
|
-
// Inner-modal wrapper styles for isHeaderInModal: the inner RNSScreenStack fills its outer
|
|
102
|
-
// RNSScreen, and the inner RNSScreen fills the stack absolutely on all four edges. This mirrors
|
|
103
|
-
// react-native-screens' own `styles.container` / `StyleSheet.absoluteFill` for this exact pair.
|
|
104
|
-
const HEADER_IN_MODAL_STACK_STYLE = { flex: 1 };
|
|
105
|
-
const HEADER_IN_MODAL_SCREEN_STYLE = { position: 'absolute', top: 0, start: 0, end: 0, bottom: 0 };
|
|
106
|
-
export function resolveHeaderInModalStackStyle() {
|
|
107
|
-
return HEADER_IN_MODAL_STACK_STYLE;
|
|
108
|
-
}
|
|
109
|
-
export function resolveHeaderInModalScreenStyle() {
|
|
110
|
-
return HEADER_IN_MODAL_SCREEN_STYLE;
|
|
111
|
-
}
|
|
112
|
-
// The RNSScreen leaf's own props; the adapter spreads this record directly onto the element it
|
|
113
|
-
// builds and appends the header config child and real screen content itself, not here.
|
|
114
|
-
export function resolveScreenProps(view) {
|
|
115
|
-
return {
|
|
116
|
-
...view.passthrough,
|
|
117
|
-
screenId: view.screenId,
|
|
118
|
-
activityState: view.activityState,
|
|
119
|
-
gestureEnabled: view.gestureEnabled,
|
|
120
|
-
stackAnimation: view.stackAnimation ?? STACK_DEFAULT_ANIMATION,
|
|
121
|
-
stackPresentation: view.stackPresentation ?? STACK_DEFAULT_PRESENTATION,
|
|
122
|
-
// Off by default upstream; a formSheet's updateBounds pushes the sheet's live native size
|
|
123
|
-
// into Fabric's shadow state on every drag frame, and this switches that push from
|
|
124
|
-
// Asynchronous to EventQueue::UpdateMode::unstable_Immediate. The ScrollView-frame correction
|
|
125
|
-
// that fills a taller detent (RNSScreenContentWrapper.mm's
|
|
126
|
-
// coerceChildScrollViewComponentSizeToSize) is driven by that same state, so an async push
|
|
127
|
-
// likely lags one step behind a fast drag. Scoped to formSheet: push/plain-modal screens have
|
|
128
|
-
// no comparable live-resize need.
|
|
129
|
-
synchronousShadowStateUpdatesEnabled: view.stackPresentation === 'formSheet',
|
|
130
|
-
transitionDuration: view.transitionDuration,
|
|
131
|
-
sheetAllowedDetents: view.sheetAllowedDetents,
|
|
132
|
-
// Native RNSScreen's real prop names carry an "Index" suffix (react-native-screens' own
|
|
133
|
-
// types.tsx); without it the value never reaches the native view and silently falls back to
|
|
134
|
-
// RNS's default.
|
|
135
|
-
sheetLargestUndimmedDetentIndex: view.sheetLargestUndimmedDetent,
|
|
136
|
-
sheetInitialDetentIndex: view.sheetInitialDetent,
|
|
137
|
-
sheetGrabberVisible: view.sheetGrabberVisible,
|
|
138
|
-
sheetCornerRadius: view.sheetCornerRadius,
|
|
139
|
-
sheetExpandsWhenScrolledToEdge: view.sheetExpandsWhenScrolledToEdge,
|
|
140
|
-
sheetElevation: view.sheetElevation,
|
|
141
|
-
sheetShouldOverflowTopInset: view.sheetShouldOverflowTopInset,
|
|
142
|
-
sheetDefaultResizeAnimationEnabled: view.sheetDefaultResizeAnimationEnabled,
|
|
143
|
-
statusBarStyle: view.statusBarStyle,
|
|
144
|
-
statusBarHidden: view.statusBarHidden,
|
|
145
|
-
statusBarAnimation: view.statusBarAnimation,
|
|
146
|
-
screenOrientation: view.screenOrientation,
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
// `style: {flex: 1}` is ours, not react-native-screens': RNSScreenStackView sizes its nav
|
|
150
|
-
// controller to `self.bounds`, which Yoga would otherwise collapse to zero, and (unlike RN's
|
|
151
|
-
// own ScreenStack.tsx) we drive the native view directly (<third_party_rn_packages_are_react_only>).
|
|
152
|
-
export function resolveStackProps(view) {
|
|
153
|
-
return { style: { flex: 1 }, ...view.passthrough };
|
|
154
|
-
}
|
|
155
|
-
// The RNSScreenStackHeaderConfig leaf is a full Descriptor since it never hosts framework children,
|
|
156
|
-
// so the adapter bridges it verbatim (descriptorToReact/descriptorToVue) as the screen's first
|
|
157
|
-
// child, ahead of the real content. `searchBarProps` nests as an RNSSearchBar wrapped in an
|
|
158
|
-
// RNSScreenStackHeaderSubview(type: 'searchBar'), matching how react-native-screens' own JS
|
|
159
|
-
// component mounts it (see resolveSearchBarView's comment in screen-options.ts); omitted when the
|
|
160
|
-
// screen has no search bar.
|
|
161
|
-
export function renderHeaderConfig(view, searchBarProps) {
|
|
162
|
-
const props = {
|
|
163
|
-
...view.passthrough,
|
|
164
|
-
title: view.title,
|
|
165
|
-
hidden: view.hidden,
|
|
166
|
-
// Android-only (CustomToolbar.kt gates its WindowInsets padding on these; iOS's
|
|
167
|
-
// UINavigationController lays out under the safe area regardless). Upstream's JS
|
|
168
|
-
// ScreenStackHeaderConfig computes these via an EdgeInsetApplicationContext that coordinates a
|
|
169
|
-
// "consume once" top inset across nested headers; we drive RNSScreenStackHeaderConfig directly
|
|
170
|
-
// and skip that layer, so without this the native default (false) leaves the header glued
|
|
171
|
-
// under the status bar/cutout on Android. Every adapter renders one active header at a time,
|
|
172
|
-
// so consuming all four edges matches upstream's default for the common case; a genuine
|
|
173
|
-
// nested-header edge case would need upstream's same coordination.
|
|
174
|
-
consumeTopInset: !view.hidden,
|
|
175
|
-
consumeLeftInset: !view.hidden,
|
|
176
|
-
consumeRightInset: !view.hidden,
|
|
177
|
-
consumeBottomInset: !view.hidden,
|
|
178
|
-
backTitle: view.backTitle,
|
|
179
|
-
backTitleVisible: view.backTitleVisible,
|
|
180
|
-
backButtonDisplayMode: view.backButtonDisplayMode,
|
|
181
|
-
largeTitle: view.largeTitle,
|
|
182
|
-
translucent: view.translucent,
|
|
183
|
-
color: view.color,
|
|
184
|
-
titleColor: view.titleColor,
|
|
185
|
-
backgroundColor: view.backgroundColor,
|
|
186
|
-
largeTitleBackgroundColor: view.largeTitleBackgroundColor,
|
|
187
|
-
userInterfaceStyle: view.userInterfaceStyle,
|
|
188
|
-
headerLeftBarButtonItems: view.headerLeftBarButtonItems,
|
|
189
|
-
headerRightBarButtonItems: view.headerRightBarButtonItems,
|
|
190
|
-
[HEADER_ON_PRESS_BAR_BUTTON_ITEM]: view.onPressHeaderBarButtonItem,
|
|
191
|
-
[HEADER_ON_PRESS_BAR_BUTTON_MENU_ITEM]: view.onPressHeaderBarButtonMenuItem,
|
|
192
|
-
};
|
|
193
|
-
const children = searchBarProps
|
|
194
|
-
? [
|
|
195
|
-
el(RNS_SCREEN_STACK_HEADER_SUBVIEW_VIEW_NAME, { type: 'searchBar' }, [
|
|
196
|
-
el(RNS_SEARCH_BAR_VIEW_NAME, searchBarProps),
|
|
197
|
-
]),
|
|
198
|
-
]
|
|
199
|
-
: [];
|
|
200
|
-
return el(RNS_SCREEN_STACK_HEADER_CONFIG_VIEW_NAME, props, children);
|
|
201
|
-
}
|
|
202
|
-
// The RNSSearchBar leaf's static config props; event handlers ride in `passthrough`.
|
|
203
|
-
// renderHeaderConfig wraps this into the RNSSearchBar Descriptor and nests it as the header
|
|
204
|
-
// config's child.
|
|
205
|
-
export function resolveSearchBarProps(view) {
|
|
206
|
-
return {
|
|
207
|
-
...view.passthrough,
|
|
208
|
-
placeholder: view.placeholder,
|
|
209
|
-
autoCapitalize: view.autoCapitalize,
|
|
210
|
-
placement: view.placement,
|
|
211
|
-
hideWhenScrolling: view.hideWhenScrolling,
|
|
212
|
-
allowToolbarIntegration: view.allowToolbarIntegration,
|
|
213
|
-
obscureBackground: view.obscureBackground,
|
|
214
|
-
hideNavigationBar: view.hideNavigationBar,
|
|
215
|
-
cancelButtonText: view.cancelButtonText,
|
|
216
|
-
barTintColor: view.barTintColor,
|
|
217
|
-
tintColor: view.tintColor,
|
|
218
|
-
textColor: view.textColor,
|
|
219
|
-
autoFocus: view.autoFocus,
|
|
220
|
-
disableBackButtonOverride: view.disableBackButtonOverride,
|
|
221
|
-
inputType: view.inputType,
|
|
222
|
-
hintTextColor: view.hintTextColor,
|
|
223
|
-
headerIconColor: view.headerIconColor,
|
|
224
|
-
shouldShowHintSearchIcon: view.shouldShowHintSearchIcon,
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
// The 7 search-bar event keys -> option-callback passthrough: zero lifecycle dependency, so every
|
|
228
|
-
// adapter built the identical map by hand. `log` is an optional per-event hook so an adapter can
|
|
229
|
-
// still attach its own route-scoped dlog line (React/Vue do; Angular doesn't) without forking the
|
|
230
|
-
// map itself.
|
|
231
|
-
export function buildSearchBarPassthrough(options, log) {
|
|
232
|
-
return {
|
|
233
|
-
[SEARCH_BAR_ON_FOCUS]: () => {
|
|
234
|
-
log?.('search bar focused');
|
|
235
|
-
options.onFocus?.();
|
|
236
|
-
},
|
|
237
|
-
[SEARCH_BAR_ON_BLUR]: () => {
|
|
238
|
-
log?.('search bar blurred');
|
|
239
|
-
options.onBlur?.();
|
|
240
|
-
},
|
|
241
|
-
[SEARCH_BAR_ON_CHANGE_TEXT]: (event) => {
|
|
242
|
-
const { text } = event.nativeEvent;
|
|
243
|
-
const changedText = typeof text === 'string' ? text : '';
|
|
244
|
-
log?.(`search text changed: ${changedText}`);
|
|
245
|
-
options.onChangeText?.(changedText);
|
|
246
|
-
},
|
|
247
|
-
[SEARCH_BAR_ON_SEARCH_BUTTON_PRESS]: (event) => {
|
|
248
|
-
const { text } = event.nativeEvent;
|
|
249
|
-
const pressedText = typeof text === 'string' ? text : '';
|
|
250
|
-
log?.(`search button pressed: ${pressedText}`);
|
|
251
|
-
options.onSearchButtonPress?.(pressedText);
|
|
252
|
-
},
|
|
253
|
-
[SEARCH_BAR_ON_CANCEL_BUTTON_PRESS]: () => {
|
|
254
|
-
log?.('search bar cancel pressed');
|
|
255
|
-
options.onCancelButtonPress?.();
|
|
256
|
-
},
|
|
257
|
-
[SEARCH_BAR_ON_CLOSE]: () => {
|
|
258
|
-
log?.('search bar closed');
|
|
259
|
-
options.onClose?.();
|
|
260
|
-
},
|
|
261
|
-
[SEARCH_BAR_ON_OPEN]: () => {
|
|
262
|
-
log?.('search bar opened');
|
|
263
|
-
options.onOpen?.();
|
|
264
|
-
},
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
export function resolveScreenRenderPlan(input) {
|
|
268
|
-
const { options, isAndroid } = input;
|
|
269
|
-
const headerHidden = options?.headerShown === false;
|
|
270
|
-
const activityState = computeActivityState(input.index, input.routeCount);
|
|
271
|
-
const screenProps = resolveScreenProps(resolveScreenView(input.screenId, activityState, options, input.screenPassthrough));
|
|
272
|
-
const searchBarOptions = options?.headerSearchBarOptions;
|
|
273
|
-
const searchBarProps = searchBarOptions
|
|
274
|
-
? resolveSearchBarProps(resolveSearchBarView(searchBarOptions, input.searchBarPassthrough ?? {}))
|
|
275
|
-
: undefined;
|
|
276
|
-
// See renderHeaderConfig's own header comment for why `props` never reflects `searchBarProps` --
|
|
277
|
-
// only `children` does -- so passing it here unconditionally is safe even for Angular, which
|
|
278
|
-
// never reads `children` off the result.
|
|
279
|
-
const headerConfig = renderHeaderConfig(resolveHeaderConfigView(options, input.platform), searchBarProps);
|
|
280
|
-
return {
|
|
281
|
-
activityState,
|
|
282
|
-
screenViewName: resolveScreenViewName(options?.stackPresentation, isAndroid),
|
|
283
|
-
screenProps,
|
|
284
|
-
headerConfig,
|
|
285
|
-
searchBarProps,
|
|
286
|
-
contentWrapperProps: {
|
|
287
|
-
style: resolveScreenContentWrapperStyle(options?.stackPresentation, headerHidden, options?.headerTranslucent, isAndroid),
|
|
288
|
-
collapsable: false,
|
|
289
|
-
},
|
|
290
|
-
inModal: isHeaderInModal(options?.stackPresentation, headerHidden, isAndroid),
|
|
291
|
-
innerStackStyle: resolveHeaderInModalStackStyle(),
|
|
292
|
-
innerScreenStyle: resolveHeaderInModalScreenStyle(),
|
|
293
|
-
};
|
|
294
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { IDescriptor } from '@symbiote-native/components';
|
|
2
|
-
import type { IColorValue, IViewStyle, IStyleProp } from '@symbiote-native/engine';
|
|
3
|
-
import type { ITabBarIcon } from './tab-options';
|
|
4
|
-
export type ITabBarItemView = {
|
|
5
|
-
key: string;
|
|
6
|
-
focused: boolean;
|
|
7
|
-
label: string;
|
|
8
|
-
icon?: ITabBarIcon;
|
|
9
|
-
badge?: string | number;
|
|
10
|
-
activeTintColor?: IColorValue;
|
|
11
|
-
inactiveTintColor?: IColorValue;
|
|
12
|
-
passthrough: Record<string, unknown>;
|
|
13
|
-
};
|
|
14
|
-
export type ITabBarViewProps = {
|
|
15
|
-
items: readonly ITabBarItemView[];
|
|
16
|
-
style?: IStyleProp<IViewStyle>;
|
|
17
|
-
passthrough: Record<string, unknown>;
|
|
18
|
-
};
|
|
19
|
-
export declare function renderTabBarItem(item: ITabBarItemView): IDescriptor;
|
|
20
|
-
export declare function renderTabBar(view: ITabBarViewProps): IDescriptor;
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
// TabBar: the render half (framework-agnostic). A bottom-tabs bar is a PURE-JS UI - unlike
|
|
2
|
-
// Stack, which drives real native RNSScreen/RNSScreenStack views, this paints ordinary
|
|
3
|
-
// `symbiote-view`/`symbiote-text` primitives, so it needs no react-native-screens ViewConfig
|
|
4
|
-
// work at all (see the task brief in packages/navigation's skill notes). Mirrors renderSwitch's
|
|
5
|
-
// shape: pure prop-driven functions building a `Descriptor` tree from pre-resolved view props;
|
|
6
|
-
// the adapter supplies focus state, tint-color resolution inputs, and the press wiring via
|
|
7
|
-
// `passthrough` (exactly like Stack wires onDismissed/onHeaderBackButtonClicked into
|
|
8
|
-
// resolveScreenView's passthrough) - this layer never invents an onPress/responder pair itself.
|
|
9
|
-
import { el, txt } from '@symbiote-native/components';
|
|
10
|
-
// react-navigation's own bottom-tabs default to the surrounding theme's primary/text colors;
|
|
11
|
-
// symbiote's navigation package has no app-level theme yet, so these are placeholder defaults -
|
|
12
|
-
// the same iOS-system-blue/neutral-grey pair render-button.ts already uses for its default
|
|
13
|
-
// tint/disabled colors, reused here rather than inventing a third pair (no shared semantic color
|
|
14
|
-
// tokens exist yet in @symbiote-native/engine as of this writing).
|
|
15
|
-
const DEFAULT_ACTIVE_TINT_COLOR = '#007AFF';
|
|
16
|
-
const DEFAULT_INACTIVE_TINT_COLOR = '#8e8e93';
|
|
17
|
-
const BADGE_BACKGROUND_COLOR = '#FF3B30';
|
|
18
|
-
const BADGE_TEXT_COLOR = '#ffffff';
|
|
19
|
-
const TAB_BAR_HEIGHT = 49;
|
|
20
|
-
const TAB_ICON_FONT_SIZE = 20;
|
|
21
|
-
const TAB_LABEL_FONT_SIZE = 10;
|
|
22
|
-
const BADGE_SIZE = 16;
|
|
23
|
-
const BADGE_BORDER_RADIUS = BADGE_SIZE / 2;
|
|
24
|
-
const BADGE_OFFSET_TOP = -4;
|
|
25
|
-
const BADGE_OFFSET_RIGHT = -8;
|
|
26
|
-
const TAB_BAR_STYLE = {
|
|
27
|
-
flexDirection: 'row',
|
|
28
|
-
height: TAB_BAR_HEIGHT,
|
|
29
|
-
};
|
|
30
|
-
const TAB_ITEM_STYLE = {
|
|
31
|
-
flex: 1,
|
|
32
|
-
alignItems: 'center',
|
|
33
|
-
justifyContent: 'center',
|
|
34
|
-
};
|
|
35
|
-
const ICON_WRAPPER_STYLE = {
|
|
36
|
-
alignItems: 'center',
|
|
37
|
-
justifyContent: 'center',
|
|
38
|
-
};
|
|
39
|
-
const BADGE_STYLE = {
|
|
40
|
-
position: 'absolute',
|
|
41
|
-
top: BADGE_OFFSET_TOP,
|
|
42
|
-
right: BADGE_OFFSET_RIGHT,
|
|
43
|
-
minWidth: BADGE_SIZE,
|
|
44
|
-
height: BADGE_SIZE,
|
|
45
|
-
borderRadius: BADGE_BORDER_RADIUS,
|
|
46
|
-
backgroundColor: BADGE_BACKGROUND_COLOR,
|
|
47
|
-
alignItems: 'center',
|
|
48
|
-
justifyContent: 'center',
|
|
49
|
-
paddingHorizontal: 3,
|
|
50
|
-
};
|
|
51
|
-
const BADGE_TEXT_STYLE = {
|
|
52
|
-
color: BADGE_TEXT_COLOR,
|
|
53
|
-
fontSize: 8,
|
|
54
|
-
fontWeight: '600',
|
|
55
|
-
};
|
|
56
|
-
function resolveTintColor(item) {
|
|
57
|
-
if (item.focused)
|
|
58
|
-
return item.activeTintColor ?? DEFAULT_ACTIVE_TINT_COLOR;
|
|
59
|
-
return item.inactiveTintColor ?? DEFAULT_INACTIVE_TINT_COLOR;
|
|
60
|
-
}
|
|
61
|
-
// A string icon paints as a glyph (an emoji/ligature font name - same convention a caller uses
|
|
62
|
-
// for a vector-icon-font label); an IDescriptor icon (an <Image>/vector-icon element the adapter
|
|
63
|
-
// already resolved) is spliced in verbatim, exactly like renderHeaderConfig treats a pre-built
|
|
64
|
-
// Descriptor child.
|
|
65
|
-
function renderIcon(icon, color) {
|
|
66
|
-
if (icon === undefined)
|
|
67
|
-
return undefined;
|
|
68
|
-
if (typeof icon === 'string')
|
|
69
|
-
return txt({ style: { color, fontSize: TAB_ICON_FONT_SIZE } }, [icon]);
|
|
70
|
-
return icon;
|
|
71
|
-
}
|
|
72
|
-
function renderBadge(badge) {
|
|
73
|
-
if (badge === undefined)
|
|
74
|
-
return undefined;
|
|
75
|
-
return el('symbiote-view', { style: BADGE_STYLE }, [
|
|
76
|
-
txt({ style: BADGE_TEXT_STYLE }, [String(badge)]),
|
|
77
|
-
]);
|
|
78
|
-
}
|
|
79
|
-
export function renderTabBarItem(item) {
|
|
80
|
-
const color = resolveTintColor(item);
|
|
81
|
-
const icon = renderIcon(item.icon, color);
|
|
82
|
-
const badge = renderBadge(item.badge);
|
|
83
|
-
const iconChildren = [];
|
|
84
|
-
if (icon)
|
|
85
|
-
iconChildren.push(icon);
|
|
86
|
-
if (badge)
|
|
87
|
-
iconChildren.push(badge);
|
|
88
|
-
const children = [];
|
|
89
|
-
if (iconChildren.length > 0)
|
|
90
|
-
children.push(el('symbiote-view', { style: ICON_WRAPPER_STYLE }, iconChildren));
|
|
91
|
-
children.push(txt({ style: { color, fontSize: TAB_LABEL_FONT_SIZE } }, [item.label]));
|
|
92
|
-
return el('symbiote-view', { ...item.passthrough, style: TAB_ITEM_STYLE, accessibilityRole: 'tab' }, children, item.key);
|
|
93
|
-
}
|
|
94
|
-
export function renderTabBar(view) {
|
|
95
|
-
const items = view.items.map(renderTabBarItem);
|
|
96
|
-
return el('symbiote-view', { ...view.passthrough, style: [TAB_BAR_STYLE, view.style] }, items);
|
|
97
|
-
}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import type { IHeaderConfigViewProps, INavigatorPlatform, IScreenOptions, IScreenViewProps, ISearchBarOptions, ISearchBarViewProps } from './navigator-props';
|
|
2
|
-
export declare function resolveHeaderConfigView(options: IScreenOptions | undefined, platform: INavigatorPlatform, passthrough?: Record<string, unknown>): IHeaderConfigViewProps;
|
|
3
|
-
export declare function resolveScreenView(screenId: string, activityState: number, options: IScreenOptions | undefined, passthrough?: Record<string, unknown>): IScreenViewProps;
|
|
4
|
-
export declare function resolveSearchBarView(options: ISearchBarOptions | undefined, passthrough?: Record<string, unknown>): ISearchBarViewProps;
|