@oxyhq/bloom 0.79.1 → 0.81.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.
Files changed (99) hide show
  1. package/lib/commonjs/scroll/index.web.js +52 -18
  2. package/lib/commonjs/scroll/index.web.js.map +1 -1
  3. package/lib/commonjs/scroll/scrollable.web.js +9 -3
  4. package/lib/commonjs/scroll/scrollable.web.js.map +1 -1
  5. package/lib/commonjs/tabs/Tabs.js +409 -71
  6. package/lib/commonjs/tabs/Tabs.js.map +1 -1
  7. package/lib/commonjs/tabs/expo-router/RouterTabs.js +215 -0
  8. package/lib/commonjs/tabs/expo-router/RouterTabs.js.map +1 -0
  9. package/lib/commonjs/tabs/expo-router/index.js +13 -0
  10. package/lib/commonjs/tabs/expo-router/index.js.map +1 -0
  11. package/lib/commonjs/theme/color-engine/index.js +6 -0
  12. package/lib/commonjs/theme/color-engine/index.js.map +1 -1
  13. package/lib/commonjs/theme/color-engine/scheme-variants.js +29 -1
  14. package/lib/commonjs/theme/color-engine/scheme-variants.js.map +1 -1
  15. package/lib/commonjs/theme/color-policy.js +54 -10
  16. package/lib/commonjs/theme/color-policy.js.map +1 -1
  17. package/lib/commonjs/theme/color-presets.js +14 -2
  18. package/lib/commonjs/theme/color-presets.js.map +1 -1
  19. package/lib/commonjs/theme/color-scope/seed-scope.js +5 -1
  20. package/lib/commonjs/theme/color-scope/seed-scope.js.map +1 -1
  21. package/lib/module/scroll/index.web.js +52 -18
  22. package/lib/module/scroll/index.web.js.map +1 -1
  23. package/lib/module/scroll/scrollable.web.js +9 -3
  24. package/lib/module/scroll/scrollable.web.js.map +1 -1
  25. package/lib/module/tabs/Tabs.js +417 -73
  26. package/lib/module/tabs/Tabs.js.map +1 -1
  27. package/lib/module/tabs/expo-router/RouterTabs.js +210 -0
  28. package/lib/module/tabs/expo-router/RouterTabs.js.map +1 -0
  29. package/lib/module/tabs/expo-router/index.js +10 -0
  30. package/lib/module/tabs/expo-router/index.js.map +1 -0
  31. package/lib/module/theme/color-engine/index.js +1 -1
  32. package/lib/module/theme/color-engine/index.js.map +1 -1
  33. package/lib/module/theme/color-engine/scheme-variants.js +28 -1
  34. package/lib/module/theme/color-engine/scheme-variants.js.map +1 -1
  35. package/lib/module/theme/color-policy.js +53 -10
  36. package/lib/module/theme/color-policy.js.map +1 -1
  37. package/lib/module/theme/color-presets.js +14 -2
  38. package/lib/module/theme/color-presets.js.map +1 -1
  39. package/lib/module/theme/color-scope/seed-scope.js +6 -2
  40. package/lib/module/theme/color-scope/seed-scope.js.map +1 -1
  41. package/lib/typescript/commonjs/scroll/index.web.d.ts.map +1 -1
  42. package/lib/typescript/commonjs/scroll/scrollable.web.d.ts +18 -5
  43. package/lib/typescript/commonjs/scroll/scrollable.web.d.ts.map +1 -1
  44. package/lib/typescript/commonjs/tabs/Tabs.d.ts +41 -1
  45. package/lib/typescript/commonjs/tabs/Tabs.d.ts.map +1 -1
  46. package/lib/typescript/commonjs/tabs/expo-router/RouterTabs.d.ts +80 -0
  47. package/lib/typescript/commonjs/tabs/expo-router/RouterTabs.d.ts.map +1 -0
  48. package/lib/typescript/commonjs/tabs/expo-router/index.d.ts +9 -0
  49. package/lib/typescript/commonjs/tabs/expo-router/index.d.ts.map +1 -0
  50. package/lib/typescript/commonjs/tabs/types.d.ts +48 -4
  51. package/lib/typescript/commonjs/tabs/types.d.ts.map +1 -1
  52. package/lib/typescript/commonjs/theme/color-engine/index.d.ts +1 -1
  53. package/lib/typescript/commonjs/theme/color-engine/index.d.ts.map +1 -1
  54. package/lib/typescript/commonjs/theme/color-engine/scheme-variants.d.ts +13 -1
  55. package/lib/typescript/commonjs/theme/color-engine/scheme-variants.d.ts.map +1 -1
  56. package/lib/typescript/commonjs/theme/color-policy.d.ts +6 -0
  57. package/lib/typescript/commonjs/theme/color-policy.d.ts.map +1 -1
  58. package/lib/typescript/commonjs/theme/color-presets.d.ts +1 -1
  59. package/lib/typescript/commonjs/theme/color-presets.d.ts.map +1 -1
  60. package/lib/typescript/commonjs/theme/color-scope/seed-scope.d.ts.map +1 -1
  61. package/lib/typescript/module/scroll/index.web.d.ts.map +1 -1
  62. package/lib/typescript/module/scroll/scrollable.web.d.ts +18 -5
  63. package/lib/typescript/module/scroll/scrollable.web.d.ts.map +1 -1
  64. package/lib/typescript/module/tabs/Tabs.d.ts +41 -1
  65. package/lib/typescript/module/tabs/Tabs.d.ts.map +1 -1
  66. package/lib/typescript/module/tabs/expo-router/RouterTabs.d.ts +80 -0
  67. package/lib/typescript/module/tabs/expo-router/RouterTabs.d.ts.map +1 -0
  68. package/lib/typescript/module/tabs/expo-router/index.d.ts +9 -0
  69. package/lib/typescript/module/tabs/expo-router/index.d.ts.map +1 -0
  70. package/lib/typescript/module/tabs/types.d.ts +48 -4
  71. package/lib/typescript/module/tabs/types.d.ts.map +1 -1
  72. package/lib/typescript/module/theme/color-engine/index.d.ts +1 -1
  73. package/lib/typescript/module/theme/color-engine/index.d.ts.map +1 -1
  74. package/lib/typescript/module/theme/color-engine/scheme-variants.d.ts +13 -1
  75. package/lib/typescript/module/theme/color-engine/scheme-variants.d.ts.map +1 -1
  76. package/lib/typescript/module/theme/color-policy.d.ts +6 -0
  77. package/lib/typescript/module/theme/color-policy.d.ts.map +1 -1
  78. package/lib/typescript/module/theme/color-presets.d.ts +1 -1
  79. package/lib/typescript/module/theme/color-presets.d.ts.map +1 -1
  80. package/lib/typescript/module/theme/color-scope/seed-scope.d.ts.map +1 -1
  81. package/package.json +15 -1
  82. package/src/__tests__/Tabs.test.tsx +470 -7
  83. package/src/__tests__/optional-peer-imports.test.ts +5 -0
  84. package/src/__tests__/scroll-web.test.tsx +255 -2
  85. package/src/__tests__/theme.test.ts +2 -1
  86. package/src/scroll/index.web.tsx +55 -19
  87. package/src/scroll/scrollable.web.ts +30 -8
  88. package/src/tabs/Tabs.tsx +518 -83
  89. package/src/tabs/expo-router/RouterTabs.tsx +268 -0
  90. package/src/tabs/expo-router/index.ts +8 -0
  91. package/src/tabs/types.ts +48 -4
  92. package/src/theme/__tests__/__fixtures__/golden-resolved-tokens.json +120 -0
  93. package/src/theme/__tests__/__snapshots__/visual-gallery.test.tsx.snap +70 -0
  94. package/src/theme/__tests__/policy-legibility.test.ts +12 -4
  95. package/src/theme/color-engine/index.ts +1 -1
  96. package/src/theme/color-engine/scheme-variants.ts +38 -1
  97. package/src/theme/color-policy.ts +68 -14
  98. package/src/theme/color-presets.ts +11 -2
  99. package/src/theme/color-scope/seed-scope.ts +6 -2
@@ -8,17 +8,30 @@ export interface ResolvedScroller {
8
8
  getOffset: () => number;
9
9
  setOffset: (offset: number) => void;
10
10
  /**
11
- * Whether the scroll container can currently hold a non-zero offset, i.e.
12
- * its content is taller than its viewport (`scrollHeight > clientHeight`).
11
+ * The largest offset the container can currently hold `scrollHeight -
12
+ * clientHeight`, clamped at 0. Answers two questions the save path needs:
13
+ * whether a 0 read is real (`maxOffset > 0`) and whether an offset is sitting
14
+ * exactly at the bottom, which is the signature of a browser clamp.
13
15
  *
14
16
  * React Navigation's web stack collapses a hidden background screen so its
15
17
  * content height drops to the viewport height; while collapsed the container
16
18
  * cannot be scrolled and its `scrollTop` is forced to 0. The hook uses this
17
19
  * to ignore a spurious 0 read coming from a collapsed container rather than
18
- * persisting it over a previously-saved good offset. The `'window'` scroller
19
- * is never collapsed by the navigator, so it always reports `true`.
20
+ * persisting it over a previously-saved good offset.
21
+ *
22
+ * The DOCUMENT collapses the same way, so the `'window'` sentinel answers the
23
+ * same question honestly rather than hardcoding `true`. It used to, on the
24
+ * premise that the navigator never collapses the document — false under a
25
+ * tabbed navigator, where every tab stays MOUNTED and non-focused ones are
26
+ * `display: none` (expo-router's `ui/TabSlot`, and react-native-screens' web
27
+ * `Screen` at `activityState === 0`). Measured in Chrome 150: switching from
28
+ * a tall tab to a short one drops `documentElement.scrollHeight` from 8040 to
29
+ * the 800px viewport, forces `scrollY` to 0 and dispatches two `scroll`
30
+ * events — which the outgoing tab's still-attached listener would otherwise
31
+ * persist as 0 over its real offset, because blur is emitted from an effect
32
+ * that has not run yet.
20
33
  */
21
- canScroll: () => boolean;
34
+ getMaxOffset: () => number;
22
35
  }
23
36
  /**
24
37
  * Build a {@link ResolvedScroller} for a target. The window sentinel reads and
@@ -1 +1 @@
1
- {"version":3,"file":"scrollable.web.d.ts","sourceRoot":"","sources":["../../../../src/scroll/scrollable.web.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoB,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAEzE;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,MAAM,CAAC;IACxB,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC;;;;;;;;;;OAUG;IACH,SAAS,EAAE,MAAM,OAAO,CAAC;CAC1B;AAuCD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,uBAAuB,GAAG,gBAAgB,CAyBhF"}
1
+ {"version":3,"file":"scrollable.web.d.ts","sourceRoot":"","sources":["../../../../src/scroll/scrollable.web.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoB,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAEzE;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,MAAM,CAAC;IACxB,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,YAAY,EAAE,MAAM,MAAM,CAAC;CAC5B;AAuCD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,uBAAuB,GAAG,gBAAgB,CAkChF"}
@@ -1,6 +1,46 @@
1
1
  import React from 'react';
2
2
  import type { TabsProps, TabsTriggerProps, TabsContentProps } from './types';
3
- export declare const Tabs: React.NamedExoticComponent<TabsProps>;
3
+ /**
4
+ * The imperative surface a horizontal drag gesture drives the underline through.
5
+ *
6
+ * It lives here rather than in the gesture's own code because every quantity the
7
+ * drag needs — where the neighbouring trigger is, how wide it is, whether there
8
+ * IS a neighbour — is measured layout the strip already holds and nothing
9
+ * outside it can see. The gesture supplies one number, the finger's delta; the
10
+ * strip decides what that means.
11
+ *
12
+ * Obtained via a `ref` on `Tabs`. The gesture belongs in the LAYOUT beside the
13
+ * strip, never inside a tab screen — a gesture mounted in a screen is destroyed
14
+ * by the navigation it commits, mid-release.
15
+ */
16
+ export interface TabsDragController {
17
+ /**
18
+ * Feed the raw horizontal finger delta (`translationX`).
19
+ *
20
+ * The underline travels OPPOSITE the finger, because that is where the
21
+ * incoming tab is: dragging left reveals the NEXT tab, which sits to the
22
+ * right. Travel is clamped at the neighbour — the underline stops exactly
23
+ * where it would land, so it never promises more than the release delivers —
24
+ * and at the first/last tab it rubber-bands instead.
25
+ *
26
+ * Returns the value that releasing NOW would commit, or `null` for "no
27
+ * commit": either the drag has not passed the threshold, or there is no
28
+ * neighbour in that direction.
29
+ */
30
+ drag(translationX: number): string | null;
31
+ /**
32
+ * End the drag. Pass the value `drag` last returned to keep the underline
33
+ * where the finger left it while the caller navigates; pass `null` to spring
34
+ * it home.
35
+ *
36
+ * On a commit the offset is FOLDED into the underline's base position rather
37
+ * than zeroed, so the focus change that follows springs from where the finger
38
+ * actually let go. Zeroing first would snap backwards for a frame and then
39
+ * animate forwards from the wrong place.
40
+ */
41
+ release(committed: string | null): void;
42
+ }
43
+ export declare const Tabs: React.NamedExoticComponent<TabsProps & React.RefAttributes<TabsDragController>>;
4
44
  export declare const TabsTrigger: React.NamedExoticComponent<TabsTriggerProps>;
5
45
  export declare const TabsContent: React.NamedExoticComponent<TabsContentProps>;
6
46
  //# sourceMappingURL=Tabs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Tabs.d.ts","sourceRoot":"","sources":["../../../../src/tabs/Tabs.tsx"],"names":[],"mappings":"AAAA,OAAO,KAQN,MAAM,OAAO,CAAC;AAiBf,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,gBAAgB,EAAe,MAAM,SAAS,CAAC;AA4Q1F,eAAO,MAAM,IAAI,uCAAyB,CAAC;AAG3C,eAAO,MAAM,WAAW,8CAAqB,CAAC;AAG9C,eAAO,MAAM,WAAW,8CAA0B,CAAC"}
1
+ {"version":3,"file":"Tabs.d.ts","sourceRoot":"","sources":["../../../../src/tabs/Tabs.tsx"],"names":[],"mappings":"AAAA,OAAO,KAUN,MAAM,OAAO,CAAC;AA4Bf,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,gBAAgB,EAAe,MAAM,SAAS,CAAC;AA0E1F;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;;;;;OAYG;IACH,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC1C;;;;;;;;;OASG;IACH,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;CACzC;AAikBD,eAAO,MAAM,IAAI,iFAAyB,CAAC;AAG3C,eAAO,MAAM,WAAW,8CAAqB,CAAC;AAG9C,eAAO,MAAM,WAAW,8CAA0B,CAAC"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * expo-router bindings for the in-screen tab strip.
3
+ *
4
+ * This subpath (`@oxyhq/bloom/tabs/expo-router`) is the ONLY place in the tabs
5
+ * feature allowed to import `expo-router`, which is why it is a separate entry:
6
+ * the core `@oxyhq/bloom/tabs` stays usable in an app with a different router,
7
+ * or none, and the optional peer is never pulled into a bundle that does not
8
+ * ask for it. Same split as `tab-bar` / `tab-bar/expo-router` and
9
+ * `scroll` / `scroll/expo-router`.
10
+ *
11
+ * Everything routing-shaped lives here on purpose: which tab the route means,
12
+ * what a committed swipe navigates to, and which links to warm. The strip
13
+ * itself stays presentational and knows only about measured geometry.
14
+ */
15
+ import { type ReactNode } from 'react';
16
+ import { type Href } from 'expo-router';
17
+ import type { TabsProps } from '../types';
18
+ export interface RouterTabItem {
19
+ /** Stable id for the tab. Used as a React key and a callback argument. */
20
+ value: string;
21
+ /** Tab label text. */
22
+ label: string;
23
+ /** The route this tab shows. Must be the tab's EXACT path. */
24
+ href: Href;
25
+ /** Icon rendered before the label. */
26
+ icon?: ReactNode;
27
+ /** Optional tally beside the label; `0` or omitted renders nothing. */
28
+ count?: number;
29
+ disabled?: boolean;
30
+ }
31
+ export type RouterTabsProps = Omit<TabsProps, 'value' | 'onValueChange' | 'hasSelection' | 'children'> & {
32
+ items: RouterTabItem[];
33
+ /**
34
+ * Called instead of navigating when the ALREADY-active tab is pressed.
35
+ *
36
+ * Without it a re-tap would push a second history entry for the route the
37
+ * user is already on, so Back would appear to do nothing. Re-tapping the
38
+ * active tab conventionally means "refresh" or "scroll to top", and that is
39
+ * the caller's to define.
40
+ */
41
+ onReselect?: (value: string) => void;
42
+ /**
43
+ * Enable the horizontal swipe between neighbouring tabs. Defaults to `true`.
44
+ *
45
+ * The gesture is INDICATOR-LED: the underline tracks the finger toward the
46
+ * neighbour and commits on release past a threshold, while the content stays
47
+ * where it is. That asymmetry is deliberate — the underline has somewhere
48
+ * real to go and can complete its journey, whereas under a `<Slot/>` there is
49
+ * no neighbouring screen mounted to bring in, so translating the content
50
+ * would only open a blank gutter on the trailing edge.
51
+ */
52
+ swipeEnabled?: boolean;
53
+ };
54
+ /**
55
+ * A tab strip whose selection IS the route.
56
+ *
57
+ * Render it in the LAYOUT that owns the tabs, above the `<Slot/>` that renders
58
+ * them — not inside the tab screens themselves. That placement is the whole
59
+ * point, and it is load-bearing twice over: a strip inside the screens is
60
+ * unmounted and rebuilt by every navigation, so its underline has nothing to
61
+ * animate from and the chrome blanks while the incoming route's chunk loads;
62
+ * and a GESTURE inside the screens would be destroyed by the very navigation it
63
+ * commits, mid-release.
64
+ *
65
+ * ```tsx
66
+ * // app/(app)/[username]/_layout.tsx
67
+ * <RouterTabs
68
+ * items={[
69
+ * { value: 'posts', label: 'Posts', href: `/${username}` },
70
+ * { value: 'replies', label: 'Replies', href: `/${username}/replies` },
71
+ * ]}
72
+ * />
73
+ * <Slot />
74
+ * ```
75
+ */
76
+ export declare function RouterTabs({ items, onReselect, swipeEnabled, ...tabsProps }: RouterTabsProps): import("react/jsx-runtime").JSX.Element;
77
+ export declare namespace RouterTabs {
78
+ var displayName: string;
79
+ }
80
+ //# sourceMappingURL=RouterTabs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RouterTabs.d.ts","sourceRoot":"","sources":["../../../../../src/tabs/expo-router/RouterTabs.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAA2C,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGhF,OAAO,EAAuB,KAAK,IAAI,EAAE,MAAM,aAAa,CAAC;AAG7D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C,MAAM,WAAW,aAAa;IAC5B,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,8DAA8D;IAC9D,IAAI,EAAE,IAAI,CAAC;IACX,sCAAsC;IACtC,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,MAAM,eAAe,GAAG,IAAI,CAChC,SAAS,EAGT,OAAO,GAAG,eAAe,GAAG,cAAc,GAAG,UAAU,CACxD,GAAG;IACF,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC;;;;;;;;;OASG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAuCF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,UAAU,EACV,YAAmB,EACnB,GAAG,SAAS,EACb,EAAE,eAAe,2CAwIjB;yBA7Ie,UAAU"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Entry for `@oxyhq/bloom/tabs/expo-router`. The component lives in a sibling
3
+ * module because this file is the published entry path and must stay stable;
4
+ * the split also keeps the router binding in one place, which is what the
5
+ * optional-peer allowlist asserts about.
6
+ */
7
+ export { RouterTabs } from './RouterTabs';
8
+ export type { RouterTabItem, RouterTabsProps } from './RouterTabs';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/tabs/expo-router/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC"}
@@ -1,10 +1,38 @@
1
1
  import type { StyleProp, ViewStyle, TextStyle } from 'react-native';
2
2
  export type TabsVariant = 'underline' | 'filled' | 'outlined';
3
3
  export interface TabsProps {
4
- /** The currently selected tab value. */
5
- value: string;
6
- /** Called when a tab is selected. */
7
- onValueChange: (value: string) => void;
4
+ /**
5
+ * The currently selected tab value — the CONTROLLED path, where the bar owns
6
+ * the underline and reports presses through {@link TabsProps.onValueChange}.
7
+ *
8
+ * OMIT it for the focus-driven path, where each trigger supplies its own
9
+ * {@link TabsTriggerProps.isFocused} and performs its own navigation. That is
10
+ * how `@oxyhq/bloom/tabs/expo-router` keeps the underline correct through
11
+ * deep links, browser Back and back gestures — none of which pass through a
12
+ * press handler. Providing both would put two writers on one shared value.
13
+ */
14
+ value?: string;
15
+ /** Called when a tab is selected. Controlled path only. */
16
+ onValueChange?: (value: string) => void;
17
+ /**
18
+ * Focus-driven path only: whether ANY trigger is currently focused.
19
+ *
20
+ * `false` fades the underline out where it stands. This is not the same as
21
+ * "the selection moved", and it is not hypothetical: a strip rendered by a
22
+ * LAYOUT keeps rendering while the layout's other, non-tab children are
23
+ * showing — a profile's `/followers` sits beside its tab routes under one
24
+ * layout — and without this the underline stays parked on whichever tab was
25
+ * last visited, asserting a selection that is not there.
26
+ *
27
+ * Fading is the whole response; the underline is deliberately NOT moved,
28
+ * because there is no position that means "nowhere" and travelling to a
29
+ * sentinel would drag it across every tab on the way out.
30
+ *
31
+ * `RouterTabs` sets it, so an app author never passes it by hand. Defaults to
32
+ * `true`, which is correct for the controlled path (a `value` naming no
33
+ * trigger simply leaves the underline where it was, as it always has).
34
+ */
35
+ hasSelection?: boolean;
8
36
  /** Visual variant for the tab bar. */
9
37
  variant?: TabsVariant;
10
38
  /**
@@ -26,8 +54,24 @@ export interface TabsTriggerProps {
26
54
  label: string;
27
55
  /** Icon rendered before the label. */
28
56
  icon?: React.ReactNode;
57
+ /**
58
+ * Optional tally rendered as a small muted number beside the label. Omitted
59
+ * (or `0`) renders nothing. The underline tracks the trigger's measured
60
+ * width, so a count never changes where it sits.
61
+ */
62
+ count?: number;
63
+ /**
64
+ * Whether the ROUTER considers this tab focused — the focus-driven path.
65
+ * Supplying it makes this trigger, not the bar, the writer of the shared
66
+ * underline, and suppresses {@link TabsProps.onValueChange} on press because
67
+ * navigation is then the caller's job. Leave it `undefined` on the controlled
68
+ * path.
69
+ */
70
+ isFocused?: boolean;
29
71
  /** Whether this tab is disabled. */
30
72
  disabled?: boolean;
73
+ /** Extra press handler. Runs on both paths, before any selection is reported. */
74
+ onPress?: () => void;
31
75
  style?: StyleProp<ViewStyle>;
32
76
  textStyle?: StyleProp<TextStyle>;
33
77
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/tabs/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEpE,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE9D,MAAM,WAAW,SAAS;IACxB,wCAAwC;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,sCAAsC;IACtC,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,qDAAqD;IACrD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,gBAAgB;IAC/B,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd,qBAAqB;IACrB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/tabs/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEpE,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE9D,MAAM,WAAW,SAAS;IACxB;;;;;;;;;OASG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC;;;;;;;;;;;;;;;;;OAiBG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,sCAAsC;IACtC,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,qDAAqD;IACrD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,gBAAgB;IAC/B,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd,qBAAqB;IACrB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B"}
@@ -10,7 +10,7 @@
10
10
  export { Hct } from './hct';
11
11
  export { TonalPalette } from './tonal-palette';
12
12
  export { DynamicScheme } from './dynamic-scheme';
13
- export { buildScheme, schemeVibrant, schemeExpressive, schemeTonalSpot, schemeNeutral } from './scheme-variants';
13
+ export { buildScheme, schemeVibrant, schemeExpressive, schemeTonalSpot, schemeNeutral, schemeMonochrome } from './scheme-variants';
14
14
  export type { SchemeVariant, AccentSources } from './scheme-variants';
15
15
  export { Roles } from './color-roles';
16
16
  export type { RoleName } from './color-roles';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/theme/color-engine/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,gBAAgB,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACjH,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,YAAY,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAChG,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAGtF,OAAO,EAAS,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAErD,OAAO,EAAe,KAAK,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEpE,sEAAsE;AACtE,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI/C;AAED,uCAAuC;AACvC,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKhD;AAED,yDAAyD;AACzD,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;AAElD,MAAM,WAAW,eAAe;IAC9B,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,wCAAwC;IACxC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,eAAe,GAAG,UAAU,CAuBpE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/theme/color-engine/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,gBAAgB,EAAE,eAAe,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACnI,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,YAAY,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAChG,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAGtF,OAAO,EAAS,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAErD,OAAO,EAAe,KAAK,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEpE,sEAAsE;AACtE,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI/C;AAED,uCAAuC;AACvC,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKhD;AAED,yDAAyD;AACzD,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;AAElD,MAAM,WAAW,eAAe;IAC9B,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,wCAAwC;IACxC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,eAAe,GAAG,UAAU,CAuBpE"}
@@ -9,7 +9,7 @@
9
9
  */
10
10
  import { DynamicScheme } from './dynamic-scheme';
11
11
  import type { Hct } from './hct';
12
- export type SchemeVariant = 'vivid' | 'vibrant' | 'expressive' | 'tonalSpot' | 'neutral';
12
+ export type SchemeVariant = 'vivid' | 'vibrant' | 'expressive' | 'tonalSpot' | 'neutral' | 'monochrome';
13
13
  /**
14
14
  * Optional explicit accent seeds for a scheme. When a source HCT is supplied, its
15
15
  * palette is built from that colour itself (keeping the brand hue + chroma) instead
@@ -39,6 +39,18 @@ export declare function schemeExpressive(source: Hct, isDark: boolean, contrastL
39
39
  export declare function schemeTonalSpot(source: Hct, isDark: boolean, contrastLevel: number, accents?: AccentSources): DynamicScheme;
40
40
  /** Near-greyscale — the seed hue is present only as a whisper. */
41
41
  export declare function schemeNeutral(source: Hct, isDark: boolean, contrastLevel: number, accents?: AccentSources): DynamicScheme;
42
+ /**
43
+ * No colour at all: every palette is greyscale, so the seed's hue is irrelevant
44
+ * and only its tone survives. This is the variant the `isMonochrome` role
45
+ * branches in `color-roles` were written for — they push `primary` to the far end
46
+ * of the scale (tone 0 on a light page, 100 on a dark one) rather than the
47
+ * mid-tone the chromatic curves would pick, which is what keeps a black-and-white
48
+ * theme from reading as a grey one.
49
+ *
50
+ * A pinned accent is ignored on purpose: honouring it would put one coloured
51
+ * family back into a scheme whose entire point is that nothing is coloured.
52
+ */
53
+ export declare function schemeMonochrome(source: Hct, isDark: boolean, contrastLevel: number): DynamicScheme;
42
54
  /**
43
55
  * Build a scheme by variant name.
44
56
  *
@@ -1 +1 @@
1
- {"version":3,"file":"scheme-variants.d.ts","sourceRoot":"","sources":["../../../../../src/theme/color-engine/scheme-variants.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAKjC,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,SAAS,GAAG,YAAY,GAAG,WAAW,GAAG,SAAS,CAAC;AAEzF;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC5B,eAAe,CAAC,EAAE,GAAG,CAAC;IACtB,cAAc,CAAC,EAAE,GAAG,CAAC;CACtB;AAoBD,uEAAuE;AACvE,wBAAgB,aAAa,CAC3B,MAAM,EAAE,GAAG,EACX,MAAM,EAAE,OAAO,EACf,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE,aAAkB,GAC1B,aAAa,CAsBf;AAED,gFAAgF;AAChF;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,GAAG,EACX,MAAM,EAAE,OAAO,EACf,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE,aAAkB,GAC1B,aAAa,CAsBf;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,GAAG,EACX,MAAM,EAAE,OAAO,EACf,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE,aAAkB,GAC1B,aAAa,CAsBf;AAED,yEAAyE;AACzE,wBAAgB,eAAe,CAC7B,MAAM,EAAE,GAAG,EACX,MAAM,EAAE,OAAO,EACf,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE,aAAkB,GAC1B,aAAa,CAgBf;AAED,kEAAkE;AAClE,wBAAgB,aAAa,CAC3B,MAAM,EAAE,GAAG,EACX,MAAM,EAAE,OAAO,EACf,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE,aAAkB,GAC1B,aAAa,CAgBf;AAaD;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,GAAG,EACX,MAAM,EAAE,OAAO,EACf,aAAa,SAAI,EACjB,OAAO,GAAE,aAAkB,GAC1B,aAAa,CAEf"}
1
+ {"version":3,"file":"scheme-variants.d.ts","sourceRoot":"","sources":["../../../../../src/theme/color-engine/scheme-variants.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAKjC,MAAM,MAAM,aAAa,GACrB,OAAO,GACP,SAAS,GACT,YAAY,GACZ,WAAW,GACX,SAAS,GACT,YAAY,CAAC;AAEjB;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC5B,eAAe,CAAC,EAAE,GAAG,CAAC;IACtB,cAAc,CAAC,EAAE,GAAG,CAAC;CACtB;AAoBD,uEAAuE;AACvE,wBAAgB,aAAa,CAC3B,MAAM,EAAE,GAAG,EACX,MAAM,EAAE,OAAO,EACf,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE,aAAkB,GAC1B,aAAa,CAsBf;AAED,gFAAgF;AAChF;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,GAAG,EACX,MAAM,EAAE,OAAO,EACf,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE,aAAkB,GAC1B,aAAa,CAsBf;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,GAAG,EACX,MAAM,EAAE,OAAO,EACf,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE,aAAkB,GAC1B,aAAa,CAsBf;AAED,yEAAyE;AACzE,wBAAgB,eAAe,CAC7B,MAAM,EAAE,GAAG,EACX,MAAM,EAAE,OAAO,EACf,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE,aAAkB,GAC1B,aAAa,CAgBf;AAED,kEAAkE;AAClE,wBAAgB,aAAa,CAC3B,MAAM,EAAE,GAAG,EACX,MAAM,EAAE,OAAO,EACf,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE,aAAkB,GAC1B,aAAa,CAgBf;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,GAAG,EACX,MAAM,EAAE,OAAO,EACf,aAAa,EAAE,MAAM,GACpB,aAAa,CAaf;AAcD;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,GAAG,EACX,MAAM,EAAE,OAAO,EACf,aAAa,SAAI,EACjB,OAAO,GAAE,aAAkB,GAC1B,aAAa,CAEf"}
@@ -37,6 +37,12 @@ export interface PinnedAccents {
37
37
  }
38
38
  /** The tokens the policy owns. Everything else keeps coming from the M3 roles. */
39
39
  export type PolicyTokens = Record<string, string>;
40
+ /**
41
+ * Whether a seed carries no colour to derive a theme from — the one authority for
42
+ * that question, so the policy and the scheme choice can never disagree about a
43
+ * given seed and produce a grey palette wearing chromatic surfaces.
44
+ */
45
+ export declare function isColourlessSeed(seedHex: string): boolean;
40
46
  /**
41
47
  * Build the policy-owned tokens for a seed and mode.
42
48
  *
@@ -1 +1 @@
1
- {"version":3,"file":"color-policy.d.ts","sourceRoot":"","sources":["../../../../src/theme/color-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAAe,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAK9D,sFAAsF;AACtF,eAAO,MAAM,YAAY;;;;;CAKf,CAAC;AAEX,wFAAwF;AACxF,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,kFAAkF;AAClF,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAkPlD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,OAAO,EACf,KAAK,EAAE,UAAU,EACjB,MAAM,GAAE,aAAkB,GACzB,YAAY,CAgHd"}
1
+ {"version":3,"file":"color-policy.d.ts","sourceRoot":"","sources":["../../../../src/theme/color-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAAe,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAK9D,sFAAsF;AACtF,eAAO,MAAM,YAAY;;;;;CAKf,CAAC;AAEX,wFAAwF;AACxF,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,kFAAkF;AAClF,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAiGlD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEzD;AAkKD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,OAAO,EACf,KAAK,EAAE,UAAU,EACjB,MAAM,GAAE,aAAkB,GACzB,YAAY,CA8Id"}
@@ -1,5 +1,5 @@
1
1
  import type { SchemeVariant } from './color-engine';
2
- export type AppColorName = 'teal' | 'blue' | 'green' | 'amber' | 'yellow' | 'red' | 'purple' | 'pink' | 'sky' | 'orange' | 'mint' | 'oxy' | 'faircoin';
2
+ export type AppColorName = 'teal' | 'blue' | 'green' | 'amber' | 'yellow' | 'red' | 'purple' | 'pink' | 'sky' | 'orange' | 'mint' | 'oxy' | 'faircoin' | 'mono';
3
3
  /**
4
4
  * A single colour preset: a brand SEED colour plus the tonal scheme variant to
5
5
  * derive its full role set with.
@@ -1 +1 @@
1
- {"version":3,"file":"color-presets.d.ts","sourceRoot":"","sources":["../../../../src/theme/color-presets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpD,MAAM,MAAM,YAAY,GACpB,MAAM,GACN,MAAM,GACN,OAAO,GACP,OAAO,GACP,QAAQ,GACR,KAAK,GACL,QAAQ,GACR,MAAM,GACN,KAAK,GACL,QAAQ,GACR,MAAM,GACN,KAAK,GACL,UAAU,CAAC;AAEf;;;;;;;;;;GAUG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,YAAY,CAAC;IACnB,kFAAkF;IAClF,GAAG,EAAE,MAAM,CAAC;IACZ,8DAA8D;IAC9D,OAAO,EAAE,aAAa,CAAC;IACvB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAElD,eAAO,MAAM,eAAe,EAAE,SAAS,YAAY,EAAsH,CAAC;AAE1K,wEAAwE;AACxE,eAAO,MAAM,mBAAmB,EAAE,SAAS,YAAY,EAAwB,CAAC;AAEhF,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAczD,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CAE3D;AAED;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,YAAY,EAAE,cAAc,CAclE,CAAC"}
1
+ {"version":3,"file":"color-presets.d.ts","sourceRoot":"","sources":["../../../../src/theme/color-presets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpD,MAAM,MAAM,YAAY,GACpB,MAAM,GACN,MAAM,GACN,OAAO,GACP,OAAO,GACP,QAAQ,GACR,KAAK,GACL,QAAQ,GACR,MAAM,GACN,KAAK,GACL,QAAQ,GACR,MAAM,GACN,KAAK,GACL,UAAU,GACV,MAAM,CAAC;AAEX;;;;;;;;;;GAUG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,YAAY,CAAC;IACnB,kFAAkF;IAClF,GAAG,EAAE,MAAM,CAAC;IACZ,8DAA8D;IAC9D,OAAO,EAAE,aAAa,CAAC;IACvB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAElD,eAAO,MAAM,eAAe,EAAE,SAAS,YAAY,EAA8H,CAAC;AAElL,wEAAwE;AACxE,eAAO,MAAM,mBAAmB,EAAE,SAAS,YAAY,EAAwB,CAAC;AAEhF,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAezD,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CAE3D;AAED;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,YAAY,EAAE,cAAc,CAqBlE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"seed-scope.d.ts","sourceRoot":"","sources":["../../../../../src/theme/color-scope/seed-scope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAI1F;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA4C9E;AAED,mEAAmE;AACnE,MAAM,WAAW,gBAAgB;IAC/B,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;IACvB;;;;OAIG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,uDAAuD;IACvD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA+BpF"}
1
+ {"version":3,"file":"seed-scope.d.ts","sourceRoot":"","sources":["../../../../../src/theme/color-scope/seed-scope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAI1F;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA4C9E;AAED,mEAAmE;AACnE,MAAM,WAAW,gBAAgB;IAC/B,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;IACvB;;;;OAIG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,uDAAuD;IACvD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAmCpF"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/scroll/index.web.tsx"],"names":[],"mappings":"AA6CA,OAAO,KAAK,EACV,wBAAwB,EACxB,uBAAuB,EACvB,2BAA2B,EAC5B,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;AAEtD,YAAY,EACV,iBAAiB,EACjB,gBAAgB,EAChB,wBAAwB,EACxB,8BAA8B,EAC9B,uBAAuB,EACvB,mBAAmB,EACnB,2BAA2B,GAC5B,MAAM,SAAS,CAAC;AA0DjB;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,uBAAuB,EAC/B,OAAO,CAAC,EAAE,2BAA2B,GACpC,wBAAwB,CA4J1B"}
1
+ {"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/scroll/index.web.tsx"],"names":[],"mappings":"AA6CA,OAAO,KAAK,EACV,wBAAwB,EACxB,uBAAuB,EACvB,2BAA2B,EAC5B,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;AAEtD,YAAY,EACV,iBAAiB,EACjB,gBAAgB,EAChB,wBAAwB,EACxB,8BAA8B,EAC9B,uBAAuB,EACvB,mBAAmB,EACnB,2BAA2B,GAC5B,MAAM,SAAS,CAAC;AA0DjB;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,uBAAuB,EAC/B,OAAO,CAAC,EAAE,2BAA2B,GACpC,wBAAwB,CAgM1B"}
@@ -8,17 +8,30 @@ export interface ResolvedScroller {
8
8
  getOffset: () => number;
9
9
  setOffset: (offset: number) => void;
10
10
  /**
11
- * Whether the scroll container can currently hold a non-zero offset, i.e.
12
- * its content is taller than its viewport (`scrollHeight > clientHeight`).
11
+ * The largest offset the container can currently hold `scrollHeight -
12
+ * clientHeight`, clamped at 0. Answers two questions the save path needs:
13
+ * whether a 0 read is real (`maxOffset > 0`) and whether an offset is sitting
14
+ * exactly at the bottom, which is the signature of a browser clamp.
13
15
  *
14
16
  * React Navigation's web stack collapses a hidden background screen so its
15
17
  * content height drops to the viewport height; while collapsed the container
16
18
  * cannot be scrolled and its `scrollTop` is forced to 0. The hook uses this
17
19
  * to ignore a spurious 0 read coming from a collapsed container rather than
18
- * persisting it over a previously-saved good offset. The `'window'` scroller
19
- * is never collapsed by the navigator, so it always reports `true`.
20
+ * persisting it over a previously-saved good offset.
21
+ *
22
+ * The DOCUMENT collapses the same way, so the `'window'` sentinel answers the
23
+ * same question honestly rather than hardcoding `true`. It used to, on the
24
+ * premise that the navigator never collapses the document — false under a
25
+ * tabbed navigator, where every tab stays MOUNTED and non-focused ones are
26
+ * `display: none` (expo-router's `ui/TabSlot`, and react-native-screens' web
27
+ * `Screen` at `activityState === 0`). Measured in Chrome 150: switching from
28
+ * a tall tab to a short one drops `documentElement.scrollHeight` from 8040 to
29
+ * the 800px viewport, forces `scrollY` to 0 and dispatches two `scroll`
30
+ * events — which the outgoing tab's still-attached listener would otherwise
31
+ * persist as 0 over its real offset, because blur is emitted from an effect
32
+ * that has not run yet.
20
33
  */
21
- canScroll: () => boolean;
34
+ getMaxOffset: () => number;
22
35
  }
23
36
  /**
24
37
  * Build a {@link ResolvedScroller} for a target. The window sentinel reads and
@@ -1 +1 @@
1
- {"version":3,"file":"scrollable.web.d.ts","sourceRoot":"","sources":["../../../../src/scroll/scrollable.web.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoB,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAEzE;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,MAAM,CAAC;IACxB,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC;;;;;;;;;;OAUG;IACH,SAAS,EAAE,MAAM,OAAO,CAAC;CAC1B;AAuCD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,uBAAuB,GAAG,gBAAgB,CAyBhF"}
1
+ {"version":3,"file":"scrollable.web.d.ts","sourceRoot":"","sources":["../../../../src/scroll/scrollable.web.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoB,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAEzE;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,MAAM,CAAC;IACxB,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,YAAY,EAAE,MAAM,MAAM,CAAC;CAC5B;AAuCD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,uBAAuB,GAAG,gBAAgB,CAkChF"}
@@ -1,6 +1,46 @@
1
1
  import React from 'react';
2
2
  import type { TabsProps, TabsTriggerProps, TabsContentProps } from './types';
3
- export declare const Tabs: React.NamedExoticComponent<TabsProps>;
3
+ /**
4
+ * The imperative surface a horizontal drag gesture drives the underline through.
5
+ *
6
+ * It lives here rather than in the gesture's own code because every quantity the
7
+ * drag needs — where the neighbouring trigger is, how wide it is, whether there
8
+ * IS a neighbour — is measured layout the strip already holds and nothing
9
+ * outside it can see. The gesture supplies one number, the finger's delta; the
10
+ * strip decides what that means.
11
+ *
12
+ * Obtained via a `ref` on `Tabs`. The gesture belongs in the LAYOUT beside the
13
+ * strip, never inside a tab screen — a gesture mounted in a screen is destroyed
14
+ * by the navigation it commits, mid-release.
15
+ */
16
+ export interface TabsDragController {
17
+ /**
18
+ * Feed the raw horizontal finger delta (`translationX`).
19
+ *
20
+ * The underline travels OPPOSITE the finger, because that is where the
21
+ * incoming tab is: dragging left reveals the NEXT tab, which sits to the
22
+ * right. Travel is clamped at the neighbour — the underline stops exactly
23
+ * where it would land, so it never promises more than the release delivers —
24
+ * and at the first/last tab it rubber-bands instead.
25
+ *
26
+ * Returns the value that releasing NOW would commit, or `null` for "no
27
+ * commit": either the drag has not passed the threshold, or there is no
28
+ * neighbour in that direction.
29
+ */
30
+ drag(translationX: number): string | null;
31
+ /**
32
+ * End the drag. Pass the value `drag` last returned to keep the underline
33
+ * where the finger left it while the caller navigates; pass `null` to spring
34
+ * it home.
35
+ *
36
+ * On a commit the offset is FOLDED into the underline's base position rather
37
+ * than zeroed, so the focus change that follows springs from where the finger
38
+ * actually let go. Zeroing first would snap backwards for a frame and then
39
+ * animate forwards from the wrong place.
40
+ */
41
+ release(committed: string | null): void;
42
+ }
43
+ export declare const Tabs: React.NamedExoticComponent<TabsProps & React.RefAttributes<TabsDragController>>;
4
44
  export declare const TabsTrigger: React.NamedExoticComponent<TabsTriggerProps>;
5
45
  export declare const TabsContent: React.NamedExoticComponent<TabsContentProps>;
6
46
  //# sourceMappingURL=Tabs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Tabs.d.ts","sourceRoot":"","sources":["../../../../src/tabs/Tabs.tsx"],"names":[],"mappings":"AAAA,OAAO,KAQN,MAAM,OAAO,CAAC;AAiBf,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,gBAAgB,EAAe,MAAM,SAAS,CAAC;AA4Q1F,eAAO,MAAM,IAAI,uCAAyB,CAAC;AAG3C,eAAO,MAAM,WAAW,8CAAqB,CAAC;AAG9C,eAAO,MAAM,WAAW,8CAA0B,CAAC"}
1
+ {"version":3,"file":"Tabs.d.ts","sourceRoot":"","sources":["../../../../src/tabs/Tabs.tsx"],"names":[],"mappings":"AAAA,OAAO,KAUN,MAAM,OAAO,CAAC;AA4Bf,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,gBAAgB,EAAe,MAAM,SAAS,CAAC;AA0E1F;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;;;;;OAYG;IACH,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC1C;;;;;;;;;OASG;IACH,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;CACzC;AAikBD,eAAO,MAAM,IAAI,iFAAyB,CAAC;AAG3C,eAAO,MAAM,WAAW,8CAAqB,CAAC;AAG9C,eAAO,MAAM,WAAW,8CAA0B,CAAC"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * expo-router bindings for the in-screen tab strip.
3
+ *
4
+ * This subpath (`@oxyhq/bloom/tabs/expo-router`) is the ONLY place in the tabs
5
+ * feature allowed to import `expo-router`, which is why it is a separate entry:
6
+ * the core `@oxyhq/bloom/tabs` stays usable in an app with a different router,
7
+ * or none, and the optional peer is never pulled into a bundle that does not
8
+ * ask for it. Same split as `tab-bar` / `tab-bar/expo-router` and
9
+ * `scroll` / `scroll/expo-router`.
10
+ *
11
+ * Everything routing-shaped lives here on purpose: which tab the route means,
12
+ * what a committed swipe navigates to, and which links to warm. The strip
13
+ * itself stays presentational and knows only about measured geometry.
14
+ */
15
+ import { type ReactNode } from 'react';
16
+ import { type Href } from 'expo-router';
17
+ import type { TabsProps } from '../types';
18
+ export interface RouterTabItem {
19
+ /** Stable id for the tab. Used as a React key and a callback argument. */
20
+ value: string;
21
+ /** Tab label text. */
22
+ label: string;
23
+ /** The route this tab shows. Must be the tab's EXACT path. */
24
+ href: Href;
25
+ /** Icon rendered before the label. */
26
+ icon?: ReactNode;
27
+ /** Optional tally beside the label; `0` or omitted renders nothing. */
28
+ count?: number;
29
+ disabled?: boolean;
30
+ }
31
+ export type RouterTabsProps = Omit<TabsProps, 'value' | 'onValueChange' | 'hasSelection' | 'children'> & {
32
+ items: RouterTabItem[];
33
+ /**
34
+ * Called instead of navigating when the ALREADY-active tab is pressed.
35
+ *
36
+ * Without it a re-tap would push a second history entry for the route the
37
+ * user is already on, so Back would appear to do nothing. Re-tapping the
38
+ * active tab conventionally means "refresh" or "scroll to top", and that is
39
+ * the caller's to define.
40
+ */
41
+ onReselect?: (value: string) => void;
42
+ /**
43
+ * Enable the horizontal swipe between neighbouring tabs. Defaults to `true`.
44
+ *
45
+ * The gesture is INDICATOR-LED: the underline tracks the finger toward the
46
+ * neighbour and commits on release past a threshold, while the content stays
47
+ * where it is. That asymmetry is deliberate — the underline has somewhere
48
+ * real to go and can complete its journey, whereas under a `<Slot/>` there is
49
+ * no neighbouring screen mounted to bring in, so translating the content
50
+ * would only open a blank gutter on the trailing edge.
51
+ */
52
+ swipeEnabled?: boolean;
53
+ };
54
+ /**
55
+ * A tab strip whose selection IS the route.
56
+ *
57
+ * Render it in the LAYOUT that owns the tabs, above the `<Slot/>` that renders
58
+ * them — not inside the tab screens themselves. That placement is the whole
59
+ * point, and it is load-bearing twice over: a strip inside the screens is
60
+ * unmounted and rebuilt by every navigation, so its underline has nothing to
61
+ * animate from and the chrome blanks while the incoming route's chunk loads;
62
+ * and a GESTURE inside the screens would be destroyed by the very navigation it
63
+ * commits, mid-release.
64
+ *
65
+ * ```tsx
66
+ * // app/(app)/[username]/_layout.tsx
67
+ * <RouterTabs
68
+ * items={[
69
+ * { value: 'posts', label: 'Posts', href: `/${username}` },
70
+ * { value: 'replies', label: 'Replies', href: `/${username}/replies` },
71
+ * ]}
72
+ * />
73
+ * <Slot />
74
+ * ```
75
+ */
76
+ export declare function RouterTabs({ items, onReselect, swipeEnabled, ...tabsProps }: RouterTabsProps): import("react/jsx-runtime").JSX.Element;
77
+ export declare namespace RouterTabs {
78
+ var displayName: string;
79
+ }
80
+ //# sourceMappingURL=RouterTabs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RouterTabs.d.ts","sourceRoot":"","sources":["../../../../../src/tabs/expo-router/RouterTabs.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAA2C,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGhF,OAAO,EAAuB,KAAK,IAAI,EAAE,MAAM,aAAa,CAAC;AAG7D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C,MAAM,WAAW,aAAa;IAC5B,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,8DAA8D;IAC9D,IAAI,EAAE,IAAI,CAAC;IACX,sCAAsC;IACtC,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,MAAM,eAAe,GAAG,IAAI,CAChC,SAAS,EAGT,OAAO,GAAG,eAAe,GAAG,cAAc,GAAG,UAAU,CACxD,GAAG;IACF,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC;;;;;;;;;OASG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAuCF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,UAAU,EACV,YAAmB,EACnB,GAAG,SAAS,EACb,EAAE,eAAe,2CAwIjB;yBA7Ie,UAAU"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Entry for `@oxyhq/bloom/tabs/expo-router`. The component lives in a sibling
3
+ * module because this file is the published entry path and must stay stable;
4
+ * the split also keeps the router binding in one place, which is what the
5
+ * optional-peer allowlist asserts about.
6
+ */
7
+ export { RouterTabs } from './RouterTabs';
8
+ export type { RouterTabItem, RouterTabsProps } from './RouterTabs';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/tabs/expo-router/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC"}