react-native-magic-tab-bar 1.0.0 → 2.0.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/README.md +253 -44
- package/lib/module/MagicTabBar.js +50 -7
- package/lib/module/MagicTabBar.js.map +1 -1
- package/lib/module/MagicTabItem.js +256 -23
- package/lib/module/MagicTabItem.js.map +1 -1
- package/lib/module/MagicTabs.js +95 -15
- package/lib/module/MagicTabs.js.map +1 -1
- package/lib/module/defaultTabs.js +5 -4
- package/lib/module/defaultTabs.js.map +1 -1
- package/lib/module/index.js +6 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/theme.js +10 -1
- package/lib/module/theme.js.map +1 -1
- package/lib/typescript/MagicTabBar.d.ts +18 -1
- package/lib/typescript/MagicTabBar.d.ts.map +1 -1
- package/lib/typescript/MagicTabItem.d.ts +30 -4
- package/lib/typescript/MagicTabItem.d.ts.map +1 -1
- package/lib/typescript/MagicTabs.d.ts +43 -6
- package/lib/typescript/MagicTabs.d.ts.map +1 -1
- package/lib/typescript/defaultTabs.d.ts +5 -4
- package/lib/typescript/defaultTabs.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +1 -2
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/theme.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +60 -0
- package/lib/typescript/types.d.ts.map +1 -1
- package/package.json +14 -4
- package/src/MagicTabBar.tsx +81 -7
- package/src/MagicTabItem.tsx +338 -19
- package/src/MagicTabs.tsx +168 -13
- package/src/defaultTabs.tsx +5 -4
- package/src/index.tsx +10 -1
- package/src/theme.ts +5 -0
- package/src/types.ts +64 -0
|
@@ -1,11 +1,37 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import type { MagicTabBarTheme, MagicTabIconProps } from './types';
|
|
2
|
+
import { View, type GestureResponderEvent } from 'react-native';
|
|
3
|
+
import type { MagicLabelMode, MagicLabelPosition, MagicTabBarTheme, MagicTabIconProps, MagicTabPressHandler } from './types';
|
|
4
4
|
export interface MagicTabItemProps {
|
|
5
5
|
/** Renders the icon. Provided automatically by `MagicTabs`. */
|
|
6
6
|
icon: (props: MagicTabIconProps) => ReactNode;
|
|
7
|
-
/** Optional label
|
|
7
|
+
/** Optional label text. */
|
|
8
8
|
label?: string;
|
|
9
|
+
/** When the label is shown: `'active'` (default), `'always'` or `'never'`. */
|
|
10
|
+
labelMode?: MagicLabelMode;
|
|
11
|
+
/** Whether the label sits to the right of the icon (default) or below it. */
|
|
12
|
+
labelPosition?: MagicLabelPosition;
|
|
13
|
+
/**
|
|
14
|
+
* Badge shown over the icon. `true` renders a dot; a number/string renders
|
|
15
|
+
* a count bubble (numbers above 99 show as `99+`). Falsy renders nothing.
|
|
16
|
+
*/
|
|
17
|
+
badge?: number | string | boolean;
|
|
18
|
+
/** Dim the tab and block presses. */
|
|
19
|
+
disabled?: boolean;
|
|
20
|
+
/** Render as a raised, circular action ("FAB") button. */
|
|
21
|
+
variant?: 'action';
|
|
22
|
+
/**
|
|
23
|
+
* Compact "light" mode: small icon-only tab, no label. Provided by
|
|
24
|
+
* `MagicTabs`. Uses its own dedicated styles.
|
|
25
|
+
*/
|
|
26
|
+
isLight?: boolean;
|
|
27
|
+
/** Fire a selection haptic on press. Requires `expo-haptics`. */
|
|
28
|
+
haptics?: boolean;
|
|
29
|
+
/** Route name, used for the press callbacks. Provided by `MagicTabs`. */
|
|
30
|
+
name?: string;
|
|
31
|
+
/** Called when the tab is pressed. */
|
|
32
|
+
onTabPress?: MagicTabPressHandler;
|
|
33
|
+
/** Called when the tab is long-pressed. */
|
|
34
|
+
onTabLongPress?: MagicTabPressHandler;
|
|
9
35
|
/** Resolved theme. Provided automatically by `MagicTabs`. */
|
|
10
36
|
theme: MagicTabBarTheme;
|
|
11
37
|
/** @internal */
|
|
@@ -24,5 +50,5 @@ export interface MagicTabItemProps {
|
|
|
24
50
|
* Each tab sizes to its content — just the icon when inactive, icon + label
|
|
25
51
|
* when active — so the active label is never clipped, on any screen width.
|
|
26
52
|
*/
|
|
27
|
-
export declare const MagicTabItem: import("react").
|
|
53
|
+
export declare const MagicTabItem: import("react").NamedExoticComponent<MagicTabItemProps & import("react").RefAttributes<View>>;
|
|
28
54
|
//# sourceMappingURL=MagicTabItem.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MagicTabItem.d.ts","sourceRoot":"","sources":["../../src/MagicTabItem.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"MagicTabItem.d.ts","sourceRoot":"","sources":["../../src/MagicTabItem.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAqD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC1F,OAAO,EAIL,IAAI,EACJ,KAAK,qBAAqB,EAE3B,MAAM,cAAc,CAAC;AAStB,OAAO,KAAK,EACV,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACrB,MAAM,SAAS,CAAC;AAmCjB,MAAM,WAAW,iBAAiB;IAChC,+DAA+D;IAC/D,IAAI,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,SAAS,CAAC;IAC9C,2BAA2B;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,6EAA6E;IAC7E,aAAa,CAAC,EAAE,kBAAkB,CAAC;IACnC;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IAClC,qCAAqC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,0DAA0D;IAC1D,OAAO,CAAC,EAAE,QAAQ,CAAC;IACnB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iEAAiE;IACjE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,UAAU,CAAC,EAAE,oBAAoB,CAAC;IAClC,2CAA2C;IAC3C,cAAc,CAAC,EAAE,oBAAoB,CAAC;IACtC,6DAA6D;IAC7D,KAAK,EAAE,gBAAgB,CAAC;IAGxB,gBAAgB;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB;IAChB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;IACjD,gBAAgB;IAChB,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;IACrD,gBAAgB;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,+FAkPvB,CAAC"}
|
|
@@ -1,14 +1,39 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import type { MagicTabBarTheme, MagicTabBarVariant, MagicTabConfig } from './types';
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import type { MagicLabelMode, MagicLabelPosition, MagicTabBarTheme, MagicTabBarVariant, MagicTabConfig, MagicTabPressHandler } from './types';
|
|
3
3
|
export interface MagicTabsProps {
|
|
4
4
|
/**
|
|
5
5
|
* The tabs to render, in order. Each entry maps a route to an icon + label.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
*
|
|
7
|
+
* Bring your own icons. For a ready-made demo set, import it explicitly:
|
|
8
|
+
* `import { defaultTabs } from 'react-native-magic-tab-bar/default-tabs'`.
|
|
8
9
|
*/
|
|
9
|
-
tabs
|
|
10
|
+
tabs: MagicTabConfig[];
|
|
10
11
|
/** Override any part of the default theme. */
|
|
11
12
|
theme?: Partial<MagicTabBarTheme>;
|
|
13
|
+
/**
|
|
14
|
+
* When to show tab labels:
|
|
15
|
+
* - `true` / `'active'` — only on the focused tab (default).
|
|
16
|
+
* - `'always'` — on every tab, all the time.
|
|
17
|
+
* - `false` / `'never'` — icon-only bar.
|
|
18
|
+
*
|
|
19
|
+
* A tab without a `label` never shows text. Individual tabs can override
|
|
20
|
+
* this via their `showLabel` config field.
|
|
21
|
+
*/
|
|
22
|
+
showLabels?: boolean | MagicLabelMode;
|
|
23
|
+
/** Place labels to the right of icons (default) or below them. */
|
|
24
|
+
labelPosition?: MagicLabelPosition;
|
|
25
|
+
/**
|
|
26
|
+
* Compact "light" mode. Off by default. When `true`, the bar is shorter,
|
|
27
|
+
* shows small icons only (labels hidden), and floats with extra bottom
|
|
28
|
+
* margin. Uses its own dedicated styles.
|
|
29
|
+
*/
|
|
30
|
+
isLight?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Extra space between the bar and the bottom edge, in "light" mode only.
|
|
33
|
+
* Added on top of the safe-area inset. Ignored unless `isLight` is `true`.
|
|
34
|
+
* Defaults to 14.
|
|
35
|
+
*/
|
|
36
|
+
lightBottomMargin?: number;
|
|
12
37
|
/** Position the bar floating over content (default) or docked in-flow. */
|
|
13
38
|
variant?: MagicTabBarVariant;
|
|
14
39
|
/**
|
|
@@ -30,6 +55,18 @@ export interface MagicTabsProps {
|
|
|
30
55
|
glass?: boolean;
|
|
31
56
|
/** Render a custom background (e.g. a blur/glass view) behind the bar. */
|
|
32
57
|
renderBackground?: () => ReactNode;
|
|
58
|
+
/**
|
|
59
|
+
* Fire a selection haptic when a tab is pressed. Requires the optional
|
|
60
|
+
* `expo-haptics` package; without it this prop has no effect. Off by default.
|
|
61
|
+
*/
|
|
62
|
+
haptics?: boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Called when any tab is pressed, with the tab's `name` and whether it was
|
|
65
|
+
* already focused — handy for "scroll to top" / "reset stack" on re-press.
|
|
66
|
+
*/
|
|
67
|
+
onTabPress?: MagicTabPressHandler;
|
|
68
|
+
/** Called when any tab is long-pressed. */
|
|
69
|
+
onTabLongPress?: MagicTabPressHandler;
|
|
33
70
|
}
|
|
34
71
|
/**
|
|
35
72
|
* A drop-in custom tab bar for Expo Router.
|
|
@@ -45,5 +82,5 @@ export interface MagicTabsProps {
|
|
|
45
82
|
* />
|
|
46
83
|
* ```
|
|
47
84
|
*/
|
|
48
|
-
export declare function MagicTabs({ tabs, theme: themeOverride, variant, isTransparent, transparency, glass, renderBackground, }: MagicTabsProps): import("react").JSX.Element;
|
|
85
|
+
export declare function MagicTabs({ tabs, theme: themeOverride, showLabels, labelPosition, isLight, lightBottomMargin, variant, isTransparent, transparency, glass, renderBackground, haptics, onTabPress, onTabLongPress, }: MagicTabsProps): import("react").JSX.Element;
|
|
49
86
|
//# sourceMappingURL=MagicTabs.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MagicTabs.d.ts","sourceRoot":"","sources":["../../src/MagicTabs.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"MagicTabs.d.ts","sourceRoot":"","sources":["../../src/MagicTabs.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAOhD,OAAO,KAAK,EACV,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,EACd,oBAAoB,EACrB,MAAM,SAAS,CAAC;AA+DjB,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,IAAI,EAAE,cAAc,EAAE,CAAC;IACvB,8CAA8C;IAC9C,KAAK,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAClC;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,OAAO,GAAG,cAAc,CAAC;IACtC,kEAAkE;IAClE,aAAa,CAAC,EAAE,kBAAkB,CAAC;IACnC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,0EAA0E;IAC1E,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,0EAA0E;IAC1E,gBAAgB,CAAC,EAAE,MAAM,SAAS,CAAC;IACnC;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,UAAU,CAAC,EAAE,oBAAoB,CAAC;IAClC,2CAA2C;IAC3C,cAAc,CAAC,EAAE,oBAAoB,CAAC;CACvC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,EACxB,IAAI,EACJ,KAAK,EAAE,aAAa,EACpB,UAAiB,EACjB,aAAuB,EACvB,OAAe,EACf,iBAAiB,EACjB,OAAO,EACP,aAAa,EACb,YAAY,EACZ,KAAK,EACL,gBAAgB,EAChB,OAAO,EACP,UAAU,EACV,cAAc,GACf,EAAE,cAAc,+BAkEhB"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { MagicTabConfig } from "./types";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* A ready-made demo tab set — Home, Explore, Notifications, Inbox and Profile,
|
|
4
|
+
* each with a matching Ionicon. Opt in via the subpath:
|
|
5
|
+
* `import { defaultTabs } from 'react-native-magic-tab-bar/default-tabs'`.
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
-
* `inbox` and `profile`.
|
|
7
|
+
* Requires `@expo/vector-icons` and assumes the app has routes named `index`
|
|
8
|
+
* (`/`), `explore`, `notifications`, `inbox` and `profile`.
|
|
8
9
|
*/
|
|
9
10
|
export declare const defaultTabs: MagicTabConfig[];
|
|
10
11
|
//# sourceMappingURL=defaultTabs.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defaultTabs.d.ts","sourceRoot":"","sources":["../../src/defaultTabs.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAqB,MAAM,SAAS,CAAC;AAcjE
|
|
1
|
+
{"version":3,"file":"defaultTabs.d.ts","sourceRoot":"","sources":["../../src/defaultTabs.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAqB,MAAM,SAAS,CAAC;AAcjE;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,EAAE,cAAc,EA+BvC,CAAC"}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
export { MagicTabs } from './MagicTabs';
|
|
2
2
|
export { MagicTabBar } from './MagicTabBar';
|
|
3
3
|
export { MagicTabItem } from './MagicTabItem';
|
|
4
|
-
export { defaultTabs } from './defaultTabs';
|
|
5
4
|
export { defaultTheme } from './theme';
|
|
6
5
|
export type { MagicTabsProps } from './MagicTabs';
|
|
7
6
|
export type { MagicTabBarProps } from './MagicTabBar';
|
|
8
7
|
export type { MagicTabItemProps } from './MagicTabItem';
|
|
9
|
-
export type { MagicTabConfig, MagicTabIconProps, MagicTabBarTheme, MagicTabBarVariant, } from './types';
|
|
8
|
+
export type { MagicTabConfig, MagicTabIconProps, MagicTabBarTheme, MagicTabBarVariant, MagicTabPressHandler, MagicLabelMode, MagicLabelPosition, MagicSpringConfig, } from './types';
|
|
10
9
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAQvC,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,YAAY,EACV,cAAc,EACd,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,EACd,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,SAAS,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/theme.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEhD,yCAAyC;AACzC,eAAO,MAAM,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/theme.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEhD,yCAAyC;AACzC,eAAO,MAAM,YAAY,EAAE,gBAgB1B,CAAC"}
|
|
@@ -17,9 +17,59 @@ export interface MagicTabConfig {
|
|
|
17
17
|
href: Href;
|
|
18
18
|
/** Optional text label rendered next to the icon while the tab is active. */
|
|
19
19
|
label?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Show this tab's label while active. Overrides the bar-level `showLabels`
|
|
22
|
+
* prop for this tab only. A tab without a `label` never shows text.
|
|
23
|
+
*/
|
|
24
|
+
showLabel?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Badge shown over the icon. `true` renders a small dot; a number or string
|
|
27
|
+
* renders a count bubble (numbers above 99 display as `99+`). Falsy values
|
|
28
|
+
* (`false`, `0`, `undefined`, `''`) render nothing.
|
|
29
|
+
*/
|
|
30
|
+
badge?: number | string | boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Dim the tab and block navigation to it. The tab still renders but cannot
|
|
33
|
+
* be pressed. Off by default.
|
|
34
|
+
*/
|
|
35
|
+
disabled?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Render this tab as a raised, circular action ("FAB") button — common for a
|
|
38
|
+
* center "create"/"compose" tab. Ignores label/pill styling and uses the
|
|
39
|
+
* theme's `actionColor`/`actionIconColor`. Defaults to a normal tab.
|
|
40
|
+
*/
|
|
41
|
+
variant?: 'action';
|
|
42
|
+
/**
|
|
43
|
+
* When this tab is the active route, switch the whole bar into compact
|
|
44
|
+
* "light" mode (short, narrow, icon-only). Lets a single scroll-heavy screen
|
|
45
|
+
* (e.g. a feed) use a minimal bar while the other tabs keep the full one. The
|
|
46
|
+
* transition between modes is animated.
|
|
47
|
+
*/
|
|
48
|
+
isLight?: boolean;
|
|
20
49
|
/** Renders the tab's icon. */
|
|
21
50
|
icon: (props: MagicTabIconProps) => ReactNode;
|
|
22
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Fired when a tab is pressed. `name` is the tab's route name and `focused`
|
|
54
|
+
* is whether that tab was already the active one (useful for "scroll to top"
|
|
55
|
+
* or "reset stack" on re-press).
|
|
56
|
+
*/
|
|
57
|
+
export type MagicTabPressHandler = (name: string, focused: boolean) => void;
|
|
58
|
+
/**
|
|
59
|
+
* When labels are shown:
|
|
60
|
+
* - `'active'` — only on the focused tab (default).
|
|
61
|
+
* - `'always'` — on every tab, all the time.
|
|
62
|
+
* - `'never'` — icon-only bar.
|
|
63
|
+
*/
|
|
64
|
+
export type MagicLabelMode = 'active' | 'always' | 'never';
|
|
65
|
+
/** Where a tab's label sits relative to its icon. */
|
|
66
|
+
export type MagicLabelPosition = 'right' | 'bottom';
|
|
67
|
+
/** Spring parameters for the tab's focus/label animations. */
|
|
68
|
+
export interface MagicSpringConfig {
|
|
69
|
+
mass: number;
|
|
70
|
+
damping: number;
|
|
71
|
+
stiffness: number;
|
|
72
|
+
}
|
|
23
73
|
/** Visual configuration for the tab bar. */
|
|
24
74
|
export interface MagicTabBarTheme {
|
|
25
75
|
/** Background color of the bar (ignored when `renderBackground` is provided). */
|
|
@@ -38,10 +88,20 @@ export interface MagicTabBarTheme {
|
|
|
38
88
|
height: number;
|
|
39
89
|
/** Corner radius of the bar and the active pill. */
|
|
40
90
|
radius: number;
|
|
91
|
+
/** Background color of a tab's badge. */
|
|
92
|
+
badgeColor: string;
|
|
93
|
+
/** Text color of a tab's badge count. */
|
|
94
|
+
badgeTextColor: string;
|
|
95
|
+
/** Background color of an `action`-variant tab. */
|
|
96
|
+
actionColor: string;
|
|
97
|
+
/** Icon color of an `action`-variant tab. */
|
|
98
|
+
actionIconColor: string;
|
|
41
99
|
/** Horizontal margin between the bar and the screen edges. */
|
|
42
100
|
horizontalMargin: number;
|
|
43
101
|
/** Extra space below the bar, added on top of the safe-area inset. */
|
|
44
102
|
bottomInset: number;
|
|
103
|
+
/** Spring used for the focus pill and label transitions. */
|
|
104
|
+
spring: MagicSpringConfig;
|
|
45
105
|
}
|
|
46
106
|
/** How the bar is positioned relative to screen content. */
|
|
47
107
|
export type MagicTabBarVariant = 'floating' | 'docked';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAExC,yDAAyD;AACzD,MAAM,WAAW,iBAAiB;IAChC,0CAA0C;IAC1C,OAAO,EAAE,OAAO,CAAC;IACjB,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;CACd;AAED,sCAAsC;AACtC,MAAM,WAAW,cAAc;IAC7B,6FAA6F;IAC7F,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,IAAI,EAAE,IAAI,CAAC;IACX,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8BAA8B;IAC9B,IAAI,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,SAAS,CAAC;CAC/C;AAED,4CAA4C;AAC5C,MAAM,WAAW,gBAAgB;IAC/B,iFAAiF;IACjF,QAAQ,EAAE,MAAM,CAAC;IACjB,0DAA0D;IAC1D,eAAe,EAAE,MAAM,CAAC;IACxB,2CAA2C;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,oCAAoC;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB,2BAA2B;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAC;IACjB,yBAAyB;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,MAAM,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,gBAAgB,EAAE,MAAM,CAAC;IACzB,sEAAsE;IACtE,WAAW,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAExC,yDAAyD;AACzD,MAAM,WAAW,iBAAiB;IAChC,0CAA0C;IAC1C,OAAO,EAAE,OAAO,CAAC;IACjB,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;CACd;AAED,sCAAsC;AACtC,MAAM,WAAW,cAAc;IAC7B,6FAA6F;IAC7F,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,IAAI,EAAE,IAAI,CAAC;IACX,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IAClC;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,OAAO,CAAC,EAAE,QAAQ,CAAC;IACnB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,8BAA8B;IAC9B,IAAI,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,SAAS,CAAC;CAC/C;AAED;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;AAE5E;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE3D,qDAAqD;AACrD,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEpD,8DAA8D;AAC9D,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,4CAA4C;AAC5C,MAAM,WAAW,gBAAgB;IAC/B,iFAAiF;IACjF,QAAQ,EAAE,MAAM,CAAC;IACjB,0DAA0D;IAC1D,eAAe,EAAE,MAAM,CAAC;IACxB,2CAA2C;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,oCAAoC;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB,2BAA2B;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAC;IACjB,yBAAyB;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,MAAM,EAAE,MAAM,CAAC;IACf,yCAAyC;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,cAAc,EAAE,MAAM,CAAC;IACvB,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAC;IACpB,6CAA6C;IAC7C,eAAe,EAAE,MAAM,CAAC;IACxB,8DAA8D;IAC9D,gBAAgB,EAAE,MAAM,CAAC;IACzB,sEAAsE;IACtE,WAAW,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,MAAM,EAAE,iBAAiB,CAAC;CAC3B;AAED,4DAA4D;AAC5D,MAAM,MAAM,kBAAkB,GAAG,UAAU,GAAG,QAAQ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-magic-tab-bar",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Customizable floating tab bar for Expo Router (SDK 56+). Bring your own icons and labels.",
|
|
5
5
|
"workspaces": [
|
|
6
6
|
"example"
|
|
@@ -9,11 +9,16 @@
|
|
|
9
9
|
"module": "./lib/module/index.js",
|
|
10
10
|
"types": "./lib/typescript/index.d.ts",
|
|
11
11
|
"source": "./src/index.tsx",
|
|
12
|
+
"sideEffects": false,
|
|
12
13
|
"exports": {
|
|
13
14
|
".": {
|
|
14
15
|
"types": "./lib/typescript/index.d.ts",
|
|
15
16
|
"default": "./lib/module/index.js"
|
|
16
17
|
},
|
|
18
|
+
"./default-tabs": {
|
|
19
|
+
"types": "./lib/typescript/defaultTabs.d.ts",
|
|
20
|
+
"default": "./lib/module/defaultTabs.js"
|
|
21
|
+
},
|
|
17
22
|
"./package.json": "./package.json"
|
|
18
23
|
},
|
|
19
24
|
"files": [
|
|
@@ -48,12 +53,11 @@
|
|
|
48
53
|
},
|
|
49
54
|
"author": "Bhavin Pethani <pethanibhavin004@gmail.com>",
|
|
50
55
|
"license": "MIT",
|
|
51
|
-
"dependencies": {
|
|
52
|
-
"@expo/vector-icons": "^15.0.2"
|
|
53
|
-
},
|
|
54
56
|
"peerDependencies": {
|
|
57
|
+
"@expo/vector-icons": "*",
|
|
55
58
|
"expo": "*",
|
|
56
59
|
"expo-glass-effect": ">=56.0.0",
|
|
60
|
+
"expo-haptics": ">=56.0.0",
|
|
57
61
|
"expo-router": ">=56.0.0",
|
|
58
62
|
"react": "*",
|
|
59
63
|
"react-native": "*",
|
|
@@ -62,11 +66,17 @@
|
|
|
62
66
|
"react-native-worklets": "*"
|
|
63
67
|
},
|
|
64
68
|
"peerDependenciesMeta": {
|
|
69
|
+
"@expo/vector-icons": {
|
|
70
|
+
"optional": true
|
|
71
|
+
},
|
|
65
72
|
"react-native-worklets": {
|
|
66
73
|
"optional": true
|
|
67
74
|
},
|
|
68
75
|
"expo-glass-effect": {
|
|
69
76
|
"optional": true
|
|
77
|
+
},
|
|
78
|
+
"expo-haptics": {
|
|
79
|
+
"optional": true
|
|
70
80
|
}
|
|
71
81
|
},
|
|
72
82
|
"devDependencies": {
|
package/src/MagicTabBar.tsx
CHANGED
|
@@ -5,8 +5,31 @@ import {
|
|
|
5
5
|
type View as RNView,
|
|
6
6
|
type ViewProps,
|
|
7
7
|
} from "react-native";
|
|
8
|
+
import Animated, { LinearTransition } from "react-native-reanimated";
|
|
8
9
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
9
|
-
import type {
|
|
10
|
+
import type {
|
|
11
|
+
MagicLabelPosition,
|
|
12
|
+
MagicTabBarTheme,
|
|
13
|
+
MagicTabBarVariant,
|
|
14
|
+
} from "./types";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Layout transition used to morph the bar between its normal and compact
|
|
18
|
+
* "light" shapes (width, height and bottom margin) when the active tab changes.
|
|
19
|
+
*/
|
|
20
|
+
const barTransition = LinearTransition.springify()
|
|
21
|
+
.mass(0.5)
|
|
22
|
+
.damping(16)
|
|
23
|
+
.stiffness(160);
|
|
24
|
+
|
|
25
|
+
/** Extra bar height when labels sit below icons, so they have room to breathe. */
|
|
26
|
+
const BOTTOM_LABEL_EXTRA_HEIGHT = 6;
|
|
27
|
+
|
|
28
|
+
/** Fixed height of the compact "light" bar. */
|
|
29
|
+
const LIGHT_BAR_HEIGHT = 46;
|
|
30
|
+
|
|
31
|
+
/** Default extra bottom margin added below the bar in "light" mode. */
|
|
32
|
+
const LIGHT_EXTRA_BOTTOM_MARGIN = 14;
|
|
10
33
|
|
|
11
34
|
declare const require: (moduleName: string) => unknown;
|
|
12
35
|
|
|
@@ -33,6 +56,12 @@ export interface MagicTabBarProps extends ViewProps {
|
|
|
33
56
|
theme: MagicTabBarTheme;
|
|
34
57
|
/** Position the bar floating over content (default) or docked in-flow. */
|
|
35
58
|
variant?: MagicTabBarVariant;
|
|
59
|
+
/**
|
|
60
|
+
* Where the tab labels sit. `'bottom'` gives the bar extra height so the
|
|
61
|
+
* stacked icon+label has room to breathe. Provided automatically by
|
|
62
|
+
* `MagicTabs`.
|
|
63
|
+
*/
|
|
64
|
+
labelPosition?: MagicLabelPosition;
|
|
36
65
|
/**
|
|
37
66
|
* Make the bar background see-through. Off by default — the bar is fully
|
|
38
67
|
* opaque. Set the strength of the effect with `transparency`.
|
|
@@ -52,6 +81,17 @@ export interface MagicTabBarProps extends ViewProps {
|
|
|
52
81
|
glass?: boolean;
|
|
53
82
|
/** Render a custom background (e.g. a blur/glass view) behind the bar. */
|
|
54
83
|
renderBackground?: () => ReactNode;
|
|
84
|
+
/**
|
|
85
|
+
* Compact "light" mode: a shorter, icon-only bar with extra bottom margin.
|
|
86
|
+
* Provided automatically by `MagicTabs`.
|
|
87
|
+
*/
|
|
88
|
+
isLight?: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Extra bottom margin below the bar in "light" mode only. Added on top of the
|
|
91
|
+
* safe-area inset and `theme.bottomInset`. Ignored when `isLight` is false.
|
|
92
|
+
* Defaults to {@link LIGHT_EXTRA_BOTTOM_MARGIN}.
|
|
93
|
+
*/
|
|
94
|
+
lightBottomMargin?: number;
|
|
55
95
|
/** The tab items. Provided automatically by `MagicTabs`. */
|
|
56
96
|
children?: ReactNode;
|
|
57
97
|
}
|
|
@@ -65,10 +105,13 @@ export const MagicTabBar = forwardRef<RNView, MagicTabBarProps>(
|
|
|
65
105
|
{
|
|
66
106
|
theme,
|
|
67
107
|
variant = "floating",
|
|
108
|
+
labelPosition = "right",
|
|
68
109
|
isTransparent = false,
|
|
69
110
|
transparency = 0.6,
|
|
70
111
|
glass = false,
|
|
71
112
|
renderBackground,
|
|
113
|
+
isLight = false,
|
|
114
|
+
lightBottomMargin = LIGHT_EXTRA_BOTTOM_MARGIN,
|
|
72
115
|
children,
|
|
73
116
|
style,
|
|
74
117
|
...rest
|
|
@@ -77,6 +120,16 @@ export const MagicTabBar = forwardRef<RNView, MagicTabBarProps>(
|
|
|
77
120
|
) {
|
|
78
121
|
const insets = useSafeAreaInsets();
|
|
79
122
|
const floating = variant !== "docked";
|
|
123
|
+
// "Light" mode is a fixed, compact height. Otherwise stacked (bottom) labels
|
|
124
|
+
// need more vertical room than the icon-only / side-by-side layouts, so the
|
|
125
|
+
// bar grows a little in that mode.
|
|
126
|
+
const barHeight = isLight
|
|
127
|
+
? LIGHT_BAR_HEIGHT
|
|
128
|
+
: labelPosition === "bottom"
|
|
129
|
+
? theme.height + BOTTOM_LABEL_EXTRA_HEIGHT
|
|
130
|
+
: theme.height;
|
|
131
|
+
// "Light" mode floats a little higher off the bottom edge.
|
|
132
|
+
const extraBottomMargin = isLight ? lightBottomMargin : 0;
|
|
80
133
|
// Native Liquid Glass needs the optional `expo-glass-effect` dep and iOS
|
|
81
134
|
// 26+; everywhere else we fall back to the translucent color background.
|
|
82
135
|
const useGlass = glass && !!glassEffect?.isLiquidGlassAvailable();
|
|
@@ -95,17 +148,23 @@ export const MagicTabBar = forwardRef<RNView, MagicTabBarProps>(
|
|
|
95
148
|
pointerEvents="box-none"
|
|
96
149
|
style={[
|
|
97
150
|
floating ? styles.floatingWrapper : styles.dockedWrapper,
|
|
151
|
+
// Light mode centers a narrower bar; drop the side padding so the
|
|
152
|
+
// bar's width is measured against the full screen width.
|
|
153
|
+
isLight && styles.lightWrapper,
|
|
98
154
|
{
|
|
99
|
-
paddingHorizontal: theme.horizontalMargin,
|
|
100
|
-
paddingBottom:
|
|
155
|
+
paddingHorizontal: isLight ? 0 : theme.horizontalMargin,
|
|
156
|
+
paddingBottom:
|
|
157
|
+
(floating ? insets.bottom : 0) + theme.bottomInset + extraBottomMargin,
|
|
101
158
|
},
|
|
102
159
|
]}
|
|
103
160
|
>
|
|
104
|
-
<View
|
|
161
|
+
<Animated.View
|
|
162
|
+
layout={barTransition}
|
|
105
163
|
style={[
|
|
106
164
|
styles.bar,
|
|
107
165
|
!seeThrough && styles.barShadow,
|
|
108
|
-
|
|
166
|
+
isLight && styles.lightBar,
|
|
167
|
+
{ height: barHeight, borderRadius: theme.radius },
|
|
109
168
|
]}
|
|
110
169
|
>
|
|
111
170
|
{renderBackground ? (
|
|
@@ -142,10 +201,10 @@ export const MagicTabBar = forwardRef<RNView, MagicTabBarProps>(
|
|
|
142
201
|
]}
|
|
143
202
|
/>
|
|
144
203
|
)}
|
|
145
|
-
<View style={[styles.row, style]} {...rest}>
|
|
204
|
+
<View style={[isLight ? styles.lightRow : styles.row, style]} {...rest}>
|
|
146
205
|
{children}
|
|
147
206
|
</View>
|
|
148
|
-
</View>
|
|
207
|
+
</Animated.View>
|
|
149
208
|
</View>
|
|
150
209
|
);
|
|
151
210
|
},
|
|
@@ -164,6 +223,13 @@ const styles = StyleSheet.create({
|
|
|
164
223
|
bar: {
|
|
165
224
|
flexDirection: "row",
|
|
166
225
|
},
|
|
226
|
+
// Light mode: a narrower bar (65% of screen width) centered by its wrapper.
|
|
227
|
+
lightWrapper: {
|
|
228
|
+
alignItems: "center",
|
|
229
|
+
},
|
|
230
|
+
lightBar: {
|
|
231
|
+
width: "65%",
|
|
232
|
+
},
|
|
167
233
|
barShadow: {
|
|
168
234
|
shadowColor: "#000",
|
|
169
235
|
shadowOpacity: 0.25,
|
|
@@ -178,4 +244,12 @@ const styles = StyleSheet.create({
|
|
|
178
244
|
justifyContent: "space-around",
|
|
179
245
|
paddingHorizontal: 6,
|
|
180
246
|
},
|
|
247
|
+
// Compact "light" row: tighter horizontal padding around the small icons.
|
|
248
|
+
lightRow: {
|
|
249
|
+
flex: 1,
|
|
250
|
+
flexDirection: "row",
|
|
251
|
+
alignItems: "center",
|
|
252
|
+
justifyContent: "space-around",
|
|
253
|
+
paddingHorizontal: 10,
|
|
254
|
+
},
|
|
181
255
|
});
|