@sigx/lynx-safe-area 0.1.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/LICENSE +21 -0
- package/README.md +217 -0
- package/android/com/sigx/safearea/SafeAreaPublisher.kt +125 -0
- package/dist/globals.d.ts +42 -0
- package/dist/globals.d.ts.map +1 -0
- package/dist/hooks.d.ts +67 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +157 -0
- package/dist/index.js.map +1 -0
- package/dist/injectable.d.ts +16 -0
- package/dist/injectable.d.ts.map +1 -0
- package/dist/provider.d.ts +44 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/safe-area-view.d.ts +37 -0
- package/dist/safe-area-view.d.ts.map +1 -0
- package/dist/types.d.ts +45 -0
- package/dist/types.d.ts.map +1 -0
- package/ios/SafeAreaPublisher.swift +189 -0
- package/package.json +44 -0
- package/sigx-module.json +14 -0
- package/src/globals.ts +70 -0
- package/src/hooks.ts +107 -0
- package/src/index.ts +30 -0
- package/src/injectable.ts +17 -0
- package/src/provider.tsx +209 -0
- package/src/safe-area-view.tsx +73 -0
- package/src/types.ts +56 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Public API for @sigx/lynx-safe-area.
|
|
2
|
+
|
|
3
|
+
export { SafeAreaProvider, SAFE_AREA_EVENT } from './provider.js';
|
|
4
|
+
export type { SafeAreaProviderProps } from './provider.js';
|
|
5
|
+
|
|
6
|
+
export { SafeAreaView } from './safe-area-view.js';
|
|
7
|
+
export type { SafeAreaViewProps } from './safe-area-view.js';
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
useSafeAreaInsets,
|
|
11
|
+
useSafeAreaSharedValues,
|
|
12
|
+
useSafeAreaFrame,
|
|
13
|
+
useSafeAreaInsetsMT,
|
|
14
|
+
} from './hooks.js';
|
|
15
|
+
|
|
16
|
+
export { useSafeAreaContext } from './injectable.js';
|
|
17
|
+
|
|
18
|
+
export {
|
|
19
|
+
readGlobalSafeArea,
|
|
20
|
+
GLOBAL_PROPS_KEY,
|
|
21
|
+
} from './globals.js';
|
|
22
|
+
export type { RawSafeAreaProps } from './globals.js';
|
|
23
|
+
|
|
24
|
+
export { ZERO_INSETS } from './types.js';
|
|
25
|
+
export type {
|
|
26
|
+
EdgeInsets,
|
|
27
|
+
Edge,
|
|
28
|
+
SafeAreaMode,
|
|
29
|
+
SafeAreaContextValue,
|
|
30
|
+
} from './types.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineInjectable } from '@sigx/lynx';
|
|
2
|
+
import type { SafeAreaContextValue } from './types.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The DI handle for the safe-area context.
|
|
6
|
+
*
|
|
7
|
+
* - Inside `<SafeAreaProvider>`'s setup we call `defineProvide(useSafeAreaContext, factory)`
|
|
8
|
+
* to install a per-app instance.
|
|
9
|
+
* - Anywhere downstream `useSafeAreaContext()` returns that instance, or
|
|
10
|
+
* `null` if no provider is in scope. Hooks defined in `./hooks.ts` wrap
|
|
11
|
+
* this with the null-check + signal subscription.
|
|
12
|
+
*
|
|
13
|
+
* The factory returns `null` at the global-singleton level so consumers
|
|
14
|
+
* outside a `<SafeAreaProvider>` get a clear signal (vs. a phantom zero-
|
|
15
|
+
* insets context that silently does nothing).
|
|
16
|
+
*/
|
|
17
|
+
export const useSafeAreaContext = defineInjectable<SafeAreaContextValue | null>(() => null);
|
package/src/provider.tsx
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import {
|
|
2
|
+
component,
|
|
3
|
+
defineProvide,
|
|
4
|
+
computed,
|
|
5
|
+
signal,
|
|
6
|
+
onMounted,
|
|
7
|
+
onUnmounted,
|
|
8
|
+
useSharedValue,
|
|
9
|
+
useMainThreadRef,
|
|
10
|
+
runOnMainThread,
|
|
11
|
+
type Define,
|
|
12
|
+
type MainThread,
|
|
13
|
+
type SharedValue,
|
|
14
|
+
} from '@sigx/lynx';
|
|
15
|
+
import { useSafeAreaContext } from './injectable.js';
|
|
16
|
+
import { readGlobalSafeArea } from './globals.js';
|
|
17
|
+
import type { EdgeInsets, SafeAreaContextValue } from './types.js';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The native publisher (iOS `SafeAreaPublisher.swift`, Android
|
|
21
|
+
* `SafeAreaPublisher.kt`) emits this event via `GlobalEventEmitter` every
|
|
22
|
+
* time it republishes insets. Payload mirrors the same `RawSafeAreaProps`
|
|
23
|
+
* shape stored under `lynx.__globalProps[GLOBAL_PROPS_KEY]`.
|
|
24
|
+
*
|
|
25
|
+
* We use a custom event rather than upstream's `onGlobalPropsChanged` so
|
|
26
|
+
* the contract stays in our hands (upstream's event-name conventions have
|
|
27
|
+
* churned across Lynx releases).
|
|
28
|
+
*/
|
|
29
|
+
export const SAFE_AREA_EVENT = 'safeAreaChanged';
|
|
30
|
+
|
|
31
|
+
interface GlobalEventEmitterLike {
|
|
32
|
+
addListener: (name: string, fn: (...a: unknown[]) => void) => void;
|
|
33
|
+
removeListener: (name: string, fn: (...a: unknown[]) => void) => void;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface LynxLike {
|
|
37
|
+
getJSModule?: (name: string) => GlobalEventEmitterLike | undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Closure-injected identifier provided by
|
|
41
|
+
// `@lynx-js/runtime-wrapper-webpack-plugin`. Same pattern as
|
|
42
|
+
// `runtime-lynx/src/shims.d.ts`. Declared locally so this package doesn't
|
|
43
|
+
// have to depend on runtime-lynx-internal just for the ambient.
|
|
44
|
+
declare const lynx: unknown | undefined;
|
|
45
|
+
|
|
46
|
+
export type SafeAreaProviderProps =
|
|
47
|
+
& Define.Prop<'class', string, false>
|
|
48
|
+
& Define.Prop<'style', Record<string, string | number>, false>
|
|
49
|
+
& Define.Slot<'default'>;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Mount once at the root of an app. Responsibilities:
|
|
53
|
+
*
|
|
54
|
+
* 1. **Seed insets synchronously** from `lynx.__globalProps[safeArea]`. The
|
|
55
|
+
* native side populates this *before* the MT bundle evaluates, so the
|
|
56
|
+
* seed is correct on first render — no flash of unsafe content.
|
|
57
|
+
*
|
|
58
|
+
* 2. **Provide a DI context** (`useSafeAreaContext`) holding:
|
|
59
|
+
* - four per-edge `SharedValue<number>`s — the single source of truth,
|
|
60
|
+
* writable on MT, observable from both threads.
|
|
61
|
+
* - a derived BG `computed<EdgeInsets>` for re-render-driven consumers
|
|
62
|
+
* (`useSafeAreaInsets()`).
|
|
63
|
+
*
|
|
64
|
+
* 3. **Subscribe to live updates** via `GlobalEventEmitter`. The native
|
|
65
|
+
* publisher emits `'safeAreaChanged'` after each `updateGlobalProps`,
|
|
66
|
+
* carrying the new inset map. We dispatch a `runOnMainThread` worklet
|
|
67
|
+
* that writes the per-edge SVs on MT — the SharedValue diff/publish
|
|
68
|
+
* bridge then propagates the new values back to the BG signal mirror,
|
|
69
|
+
* which re-fires the `computed` and re-renders consumers.
|
|
70
|
+
*
|
|
71
|
+
* 4. **Apply CSS variables** (`--sat`, `--sar`, `--sab`, `--sal`,
|
|
72
|
+
* `--safe-area-keyboard`) on the root `<view>` so utility-class
|
|
73
|
+
* consumers can write `class="pt-[var(--sat)]"` and have it work
|
|
74
|
+
* uniformly across iOS and Android (upstream's
|
|
75
|
+
* `env(safe-area-inset-*)` is iOS-only).
|
|
76
|
+
*/
|
|
77
|
+
export const SafeAreaProvider = component<SafeAreaProviderProps>(({ props, slots }) => {
|
|
78
|
+
const initial = readGlobalSafeArea();
|
|
79
|
+
|
|
80
|
+
const svTop = useSharedValue(initial.top);
|
|
81
|
+
const svRight = useSharedValue(initial.right);
|
|
82
|
+
const svBottom = useSharedValue(initial.bottom);
|
|
83
|
+
const svLeft = useSharedValue(initial.left);
|
|
84
|
+
|
|
85
|
+
// Reactive object signal for the non-SV extras (BG-only — keyboard,
|
|
86
|
+
// statusBar, navigationBar don't drive MT-bound layout, so SV plumbing
|
|
87
|
+
// isn't worth the cost). `signal({...})` returns a deeply reactive proxy;
|
|
88
|
+
// access via `extras.keyboard` etc., replace via `extras.$set({...})`.
|
|
89
|
+
const extras = signal<Extras>({
|
|
90
|
+
keyboard: initial.keyboard,
|
|
91
|
+
statusBar: initial.statusBar,
|
|
92
|
+
navigationBar: initial.navigationBar,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Single source of truth for BG consumers — derived reactively from the
|
|
96
|
+
// four edge SVs (which live on MT) and the extras signal (which lives on
|
|
97
|
+
// BG). Re-runs when MT publishes new SV values via the AvBridge OR when
|
|
98
|
+
// the safeAreaChanged listener writes to `extras`.
|
|
99
|
+
const insets = computed<EdgeInsets>(() => ({
|
|
100
|
+
top: svTop.value,
|
|
101
|
+
right: svRight.value,
|
|
102
|
+
bottom: svBottom.value,
|
|
103
|
+
left: svLeft.value,
|
|
104
|
+
keyboard: extras.keyboard,
|
|
105
|
+
statusBar: extras.statusBar,
|
|
106
|
+
navigationBar: extras.navigationBar,
|
|
107
|
+
}));
|
|
108
|
+
|
|
109
|
+
const ctx: SafeAreaContextValue = {
|
|
110
|
+
insets,
|
|
111
|
+
sv: { top: svTop, right: svRight, bottom: svBottom, left: svLeft },
|
|
112
|
+
};
|
|
113
|
+
defineProvide(useSafeAreaContext, () => ctx);
|
|
114
|
+
|
|
115
|
+
// Worklet that writes the four per-edge SVs on MT. Captured by `_c` at
|
|
116
|
+
// build time — runOnMainThread ships the SV refs as `{_wvid, _initValue}`
|
|
117
|
+
// placeholders that the MT runtime resolves to the live envelope.
|
|
118
|
+
const writeOnMT = runOnMainThread((t: number, r: number, b: number, l: number) => {
|
|
119
|
+
'main thread';
|
|
120
|
+
svTop.current.value = t;
|
|
121
|
+
svRight.current.value = r;
|
|
122
|
+
svBottom.current.value = b;
|
|
123
|
+
svLeft.current.value = l;
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Hold the elRef purely so consumers can extend the provider's host view
|
|
127
|
+
// via the published CSS variables. Not used internally for any MT writes.
|
|
128
|
+
const elRef = useMainThreadRef<MainThread.Element | null>(null);
|
|
129
|
+
|
|
130
|
+
let listener: ((...a: unknown[]) => void) | undefined;
|
|
131
|
+
let emitter: GlobalEventEmitterLike | undefined;
|
|
132
|
+
|
|
133
|
+
onMounted(() => {
|
|
134
|
+
// `lynx` is a closure-injected identifier (provided by
|
|
135
|
+
// `@lynx-js/runtime-wrapper-webpack-plugin`'s `__init_card_bundle__`
|
|
136
|
+
// wrapper), NOT a property of `globalThis`. Access as a bare identifier
|
|
137
|
+
// with `typeof` guard — same pattern as `runtime-lynx/src/bg-bridge.ts`.
|
|
138
|
+
const lynxObj: LynxLike | undefined = typeof lynx !== 'undefined'
|
|
139
|
+
? (lynx as unknown as LynxLike)
|
|
140
|
+
: undefined;
|
|
141
|
+
emitter = lynxObj?.getJSModule?.('GlobalEventEmitter');
|
|
142
|
+
if (!emitter) return;
|
|
143
|
+
listener = (raw: unknown) => {
|
|
144
|
+
const next = normaliseInsets(raw, insets.value);
|
|
145
|
+
extras.$set({
|
|
146
|
+
keyboard: next.keyboard,
|
|
147
|
+
statusBar: next.statusBar,
|
|
148
|
+
navigationBar: next.navigationBar,
|
|
149
|
+
});
|
|
150
|
+
void writeOnMT(next.top, next.right, next.bottom, next.left);
|
|
151
|
+
};
|
|
152
|
+
emitter.addListener(SAFE_AREA_EVENT, listener);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
onUnmounted(() => {
|
|
156
|
+
if (emitter && listener) emitter.removeListener(SAFE_AREA_EVENT, listener);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
return () => (
|
|
160
|
+
<view
|
|
161
|
+
class={props.class}
|
|
162
|
+
main-thread:ref={elRef}
|
|
163
|
+
style={cssVarStyle(insets.value, props.style)}
|
|
164
|
+
>
|
|
165
|
+
{slots.default?.()}
|
|
166
|
+
</view>
|
|
167
|
+
);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
interface Extras {
|
|
171
|
+
keyboard: number;
|
|
172
|
+
statusBar: number;
|
|
173
|
+
navigationBar: number;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function normaliseInsets(raw: unknown, fallback: EdgeInsets): EdgeInsets {
|
|
177
|
+
if (!raw || typeof raw !== 'object') return fallback;
|
|
178
|
+
const o = raw as Record<string, unknown>;
|
|
179
|
+
return {
|
|
180
|
+
top: numOr(o['top'], fallback.top),
|
|
181
|
+
right: numOr(o['right'], fallback.right),
|
|
182
|
+
bottom: numOr(o['bottom'], fallback.bottom),
|
|
183
|
+
left: numOr(o['left'], fallback.left),
|
|
184
|
+
keyboard: numOr(o['keyboard'], fallback.keyboard),
|
|
185
|
+
statusBar: numOr(o['statusBar'], fallback.statusBar),
|
|
186
|
+
navigationBar: numOr(o['navigationBar'], fallback.navigationBar),
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function numOr(v: unknown, fallback: number): number {
|
|
191
|
+
return typeof v === 'number' && Number.isFinite(v) ? v : fallback;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function cssVarStyle(
|
|
195
|
+
i: EdgeInsets,
|
|
196
|
+
user: Record<string, string | number> | undefined,
|
|
197
|
+
): Record<string, string | number> {
|
|
198
|
+
const base: Record<string, string | number> = {
|
|
199
|
+
'--sat': `${i.top}px`,
|
|
200
|
+
'--sar': `${i.right}px`,
|
|
201
|
+
'--sab': `${i.bottom}px`,
|
|
202
|
+
'--sal': `${i.left}px`,
|
|
203
|
+
'--safe-area-keyboard': `${i.keyboard}px`,
|
|
204
|
+
};
|
|
205
|
+
return user ? { ...base, ...user } : base;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// re-export so users only need `@sigx/lynx-safe-area`
|
|
209
|
+
export type { SharedValue };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { component, type Define } from '@sigx/lynx';
|
|
2
|
+
import { useSafeAreaInsets } from './hooks.js';
|
|
3
|
+
import type { Edge, SafeAreaMode } from './types.js';
|
|
4
|
+
|
|
5
|
+
export type SafeAreaViewProps =
|
|
6
|
+
& Define.Prop<'edges', Edge[], false>
|
|
7
|
+
& Define.Prop<'mode', SafeAreaMode, false>
|
|
8
|
+
& Define.Prop<'class', string, false>
|
|
9
|
+
& Define.Prop<'style', Record<string, string | number>, false>
|
|
10
|
+
& Define.Slot<'default'>;
|
|
11
|
+
|
|
12
|
+
const ALL_EDGES: Edge[] = ['top', 'right', 'bottom', 'left'];
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Drop-in container that applies the current safe-area insets as padding
|
|
16
|
+
* (default) or margin on the configured edges.
|
|
17
|
+
*
|
|
18
|
+
* Implementation: BG signal + inline style. Sigx auto-tracks `insets.value`
|
|
19
|
+
* access in the render function, so the inset values land in the FIRST
|
|
20
|
+
* layout pass and re-apply reactively on every `safeAreaChanged` event.
|
|
21
|
+
*
|
|
22
|
+
* The previous implementation used `useAnimatedStyle` to drive padding via
|
|
23
|
+
* the MT bridge — but `setStyleProperties` writes that affect layout fire
|
|
24
|
+
* AFTER the first layout pass, and child elements that have already laid
|
|
25
|
+
* out (notably `<scroll-view>`, which captures its frame eagerly) don't
|
|
26
|
+
* reflow. Inline style avoids that timing trap entirely.
|
|
27
|
+
*
|
|
28
|
+
* `edges` defaults to all four sides. Pass a subset (e.g. `['top']`) to
|
|
29
|
+
* leave the unspecified sides unaffected.
|
|
30
|
+
*
|
|
31
|
+
* Must be a descendant of `<SafeAreaProvider>`. If no provider is in scope
|
|
32
|
+
* (test/storybook), `useSafeAreaInsets()` returns `ZERO_INSETS` with a
|
|
33
|
+
* dev-mode warning and SafeAreaView passes through unchanged.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```tsx
|
|
37
|
+
* <SafeAreaProvider>
|
|
38
|
+
* <SafeAreaView edges={['top', 'bottom']} class="bg-base-100 flex-1">
|
|
39
|
+
* <PageContent />
|
|
40
|
+
* </SafeAreaView>
|
|
41
|
+
* </SafeAreaProvider>
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export const SafeAreaView = component<SafeAreaViewProps>(({ props, slots }) => {
|
|
45
|
+
const insets = useSafeAreaInsets();
|
|
46
|
+
const edges = props.edges ?? ALL_EDGES;
|
|
47
|
+
const mode = props.mode ?? 'padding';
|
|
48
|
+
|
|
49
|
+
return () => {
|
|
50
|
+
const i = insets.value;
|
|
51
|
+
const insetStyle: Record<string, string | number> = {};
|
|
52
|
+
if (edges.includes('top')) {
|
|
53
|
+
insetStyle[mode === 'padding' ? 'paddingTop' : 'marginTop'] = `${i.top}px`;
|
|
54
|
+
}
|
|
55
|
+
if (edges.includes('right')) {
|
|
56
|
+
insetStyle[mode === 'padding' ? 'paddingRight' : 'marginRight'] = `${i.right}px`;
|
|
57
|
+
}
|
|
58
|
+
if (edges.includes('bottom')) {
|
|
59
|
+
insetStyle[mode === 'padding' ? 'paddingBottom' : 'marginBottom'] = `${i.bottom}px`;
|
|
60
|
+
}
|
|
61
|
+
if (edges.includes('left')) {
|
|
62
|
+
insetStyle[mode === 'padding' ? 'paddingLeft' : 'marginLeft'] = `${i.left}px`;
|
|
63
|
+
}
|
|
64
|
+
return (
|
|
65
|
+
<view
|
|
66
|
+
class={props.class}
|
|
67
|
+
style={props.style ? { ...props.style, ...insetStyle } : insetStyle}
|
|
68
|
+
>
|
|
69
|
+
{slots.default?.()}
|
|
70
|
+
</view>
|
|
71
|
+
);
|
|
72
|
+
};
|
|
73
|
+
});
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-edge inset values, in dp/pt (logical pixels). Top/right/bottom/left
|
|
3
|
+
* follow CSS shorthand order. Keyboard, statusBar, navigationBar are
|
|
4
|
+
* informational extras populated when the host platform exposes them — they
|
|
5
|
+
* may be 0 if unknown.
|
|
6
|
+
*/
|
|
7
|
+
export interface EdgeInsets {
|
|
8
|
+
top: number;
|
|
9
|
+
right: number;
|
|
10
|
+
bottom: number;
|
|
11
|
+
left: number;
|
|
12
|
+
/** IME (soft keyboard) height when visible, 0 when hidden. */
|
|
13
|
+
keyboard: number;
|
|
14
|
+
/** Status-bar height (top system bar). Often equal to `top`, but on
|
|
15
|
+
* notched devices the safe-area top includes the notch and the status
|
|
16
|
+
* bar is the smaller status-only inset. */
|
|
17
|
+
statusBar: number;
|
|
18
|
+
/** Navigation-bar height (Android gesture/3-button nav at bottom). */
|
|
19
|
+
navigationBar: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The four standard CSS edges. Subset to control which sides
|
|
24
|
+
* `<SafeAreaView>` applies inset padding/margin to.
|
|
25
|
+
*/
|
|
26
|
+
export type Edge = 'top' | 'right' | 'bottom' | 'left';
|
|
27
|
+
|
|
28
|
+
/** Whether `<SafeAreaView>` applies its insets as `padding` or `margin`. */
|
|
29
|
+
export type SafeAreaMode = 'padding' | 'margin';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The injectable shape exposed by `<SafeAreaProvider>`. Components that need
|
|
33
|
+
* insets reactively read `insets.value` (BG signal) or, for MT-driven
|
|
34
|
+
* layouts, subscribe to per-edge `SharedValue`s.
|
|
35
|
+
*/
|
|
36
|
+
export interface SafeAreaContextValue {
|
|
37
|
+
/** BG-side reactive insets. Re-renders the consumer on change. */
|
|
38
|
+
readonly insets: import('@sigx/reactivity').PrimitiveSignal<EdgeInsets>;
|
|
39
|
+
/** Per-edge SharedValues for MT-driven `useAnimatedStyle` bindings. */
|
|
40
|
+
readonly sv: {
|
|
41
|
+
top: import('@sigx/lynx').SharedValue<number>;
|
|
42
|
+
right: import('@sigx/lynx').SharedValue<number>;
|
|
43
|
+
bottom: import('@sigx/lynx').SharedValue<number>;
|
|
44
|
+
left: import('@sigx/lynx').SharedValue<number>;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const ZERO_INSETS: EdgeInsets = {
|
|
49
|
+
top: 0,
|
|
50
|
+
right: 0,
|
|
51
|
+
bottom: 0,
|
|
52
|
+
left: 0,
|
|
53
|
+
keyboard: 0,
|
|
54
|
+
statusBar: 0,
|
|
55
|
+
navigationBar: 0,
|
|
56
|
+
};
|