@ng-native/router 0.13.0 → 0.14.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.
@@ -26,10 +26,12 @@
26
26
  * each activated route's component on a child view, so destroying a route cannot remove this
27
27
  * tab item from the bar.
28
28
  */
29
- import { Component, ElementRef, computed, inject, input } from '@angular/core';
29
+ import { Component, ElementRef, ErrorHandler, computed, effect, inject, input, signal, } from '@angular/core';
30
30
  import { nativePlatform, HostEngine } from '@ng-native/fabric';
31
31
  import { NATIVE_TAB_DEFAULTS } from "./native-bar-defaults.js";
32
32
  import { ownHost } from "./own-host.js";
33
+ import { optionalBoolean } from "./transforms.js";
34
+ import { SVG_ICON_HOST, SVG_ICON_RENDERER } from "./svg-icons.js";
33
35
  import * as i0 from "@angular/core";
34
36
  /**
35
37
  * An icon and its selected counterpart, as one set of props.
@@ -57,6 +59,9 @@ export function tabIconProps(icon, selectedIcon) {
57
59
  selectedImageIconResource: selectedIcon.imageIconResource,
58
60
  };
59
61
  }
62
+ function isSvg(icon) {
63
+ return icon !== undefined && 'svg' in icon;
64
+ }
60
65
  /** The paths already warned about, so a tab bar rebuilt by a navigation says it once. */
61
66
  const warnedTabs = new Set();
62
67
  /**
@@ -83,6 +88,23 @@ function warnIfIconOnOnePlatform(tab) {
83
88
  console.warn(`[angular-native] <native-tab path="${path}"> has no icon on ${platform}: ${platform} reads ` +
84
89
  `only ${missing}. Add ${missing}="...", or bind [icon] for a tab meant for one platform.`);
85
90
  }
91
+ /**
92
+ * Says, once per path and in development only, that a tab's icon is SVG and the app gave nothing
93
+ * to draw it, so the tab shows no icon.
94
+ */
95
+ function warnNoSvgRenderer(path) {
96
+ if (typeof ngDevMode !== 'undefined' && !ngDevMode)
97
+ return;
98
+ if (warnedTabs.has(`svg:${path}`))
99
+ return;
100
+ warnedTabs.add(`svg:${path}`);
101
+ console.warn(`[angular-native] <native-tab path="${path}"> has an SVG icon and nothing to draw it, so it ` +
102
+ `shows none. Add withSvgIcons(SvgImages) from @ng-native/icons to provideNativeRouter.`);
103
+ }
104
+ /** A tab bar icon's size in points when an SVG icon gives none, as each platform draws one. */
105
+ function svgIconSize() {
106
+ return nativePlatform() === 'android' ? 24 : 25;
107
+ }
86
108
  /**
87
109
  * The key native identifies a tab by: its path, or `/` for the tab at path `''`, since iOS reads an
88
110
  * empty key as none and fails on the tap that selects it. No route path starts with `/`.
@@ -94,6 +116,9 @@ export class NativeTab {
94
116
  engine = inject(HostEngine);
95
117
  /** The app's `withTabDefaults`, for an appearance this tab does not bind itself. */
96
118
  defaults = inject(NATIVE_TAB_DEFAULTS);
119
+ svgRenderer = inject(SVG_ICON_RENDERER);
120
+ svgHost = inject(SVG_ICON_HOST, { optional: true });
121
+ errors = inject(ErrorHandler);
97
122
  /**
98
123
  * The `RNSTabsScreen` this tab is. The outlet creates the activated route's component inside
99
124
  * it, which is why it is public: a tab is a slot as well as a bar item.
@@ -128,6 +153,24 @@ export class NativeTab {
128
153
  /** One of Apple's standard items, which supplies its own icon and title. */
129
154
  systemItem = input(/* @ts-ignore */
130
155
  ...(ngDevMode ? [undefined, { debugName: "systemItem" }] : /* istanbul ignore next */ []));
156
+ /**
157
+ * iOS 26.1 and later: `search` makes this the bar's search tab, a `UISearchTab`, set apart at the
158
+ * trailing end. Needs react-native-screens 4.29 or later; an older one ignores it.
159
+ *
160
+ * Native reads it once, when the tab is created, and asserts in a debug build if it changes
161
+ * afterwards, so bind a value that does not. A bar has at most one search tab. Built with the
162
+ * iOS 26 SDK, as Expo Go is, `systemItem="search"` already gets the detached button; an app
163
+ * built with the iOS 27 SDK needs this, and on iOS 27 `automaticallyActivatesSearch` as well.
164
+ */
165
+ role = input(/* @ts-ignore */
166
+ ...(ngDevMode ? [undefined, { debugName: "role" }] : /* istanbul ignore next */ []));
167
+ /**
168
+ * iOS 26.1 and later, for a `role="search"` tab: selecting the tab focuses the search field of
169
+ * the `<native-search-bar>` in the header of the stack inside it, which that stack has to have.
170
+ * On iOS 27 the tab is detached only while this is on, and the tab's first visit does not focus
171
+ * the field. Expo Go's react-native-screens is older and ignores it.
172
+ */
173
+ automaticallyActivatesSearch = input(undefined, { ...(ngDevMode ? { debugName: "automaticallyActivatesSearch" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
131
174
  /** The badge on the item: a count, or a single character for a dot. */
132
175
  badge = input(/* @ts-ignore */
133
176
  ...(ngDevMode ? [undefined, { debugName: "badge" }] : /* istanbul ignore next */ []));
@@ -147,8 +190,42 @@ export class NativeTab {
147
190
  ...(ngDevMode ? [{ debugName: "standard" }] : /* istanbul ignore next */ []));
148
191
  scrollEdge = computed(() => this.processAppearance(this.scrollEdgeAppearance() ?? this.defaults().scrollEdgeAppearance), /* @ts-ignore */
149
192
  ...(ngDevMode ? [{ debugName: "scrollEdge" }] : /* istanbul ignore next */ []));
150
- icons = computed(() => tabIconProps(this.iconProps(this.icon() ?? this.shorthand()), this.iconProps(this.selectedIcon())), /* @ts-ignore */
193
+ /** `icon` and `selectedIcon` as drawn, when they are SVG. */
194
+ drawnIcon = this.drawn(this.icon);
195
+ drawnSelectedIcon = this.drawn(this.selectedIcon);
196
+ icons = computed(() => {
197
+ const icon = this.iconProps(this.icon() ?? this.shorthand(), this.drawnIcon());
198
+ // A selected icon drawn before the icon it replaces waits for it: native takes no pair of a
199
+ // selected icon and none.
200
+ const undrawn = isSvg(this.icon()) && this.drawnIcon() === undefined;
201
+ const selected = undrawn ? {} : this.iconProps(this.selectedIcon(), this.drawnSelectedIcon());
202
+ return tabIconProps(icon, selected);
203
+ }, /* @ts-ignore */
151
204
  ...(ngDevMode ? [{ debugName: "icons" }] : /* istanbul ignore next */ []));
205
+ /**
206
+ * An SVG icon as the renderer drew it, undefined until it has. Markup that changes is drawn
207
+ * again, and a drawing that finishes after newer markup arrived is dropped.
208
+ */
209
+ drawn(icon) {
210
+ const markup = computed(() => {
211
+ const svg = icon();
212
+ return isSvg(svg) ? { svg: svg.svg, size: svg.size ?? svgIconSize() } : undefined;
213
+ }, { ...(ngDevMode ? { debugName: "markup" } : /* istanbul ignore next */ {}), equal: (a, b) => a?.svg === b?.svg && a?.size === b?.size });
214
+ const drawn = signal(undefined, /* @ts-ignore */
215
+ ...(ngDevMode ? [{ debugName: "drawn" }] : /* istanbul ignore next */ []));
216
+ effect((onCleanup) => {
217
+ const wanted = markup();
218
+ drawn.set(undefined);
219
+ if (!wanted || !this.svgHost)
220
+ return;
221
+ if (!this.svgRenderer)
222
+ return warnNoSvgRenderer(this.path());
223
+ let current = true;
224
+ onCleanup(() => (current = false));
225
+ this.svgRenderer.render(wanted.svg, wanted.size, this.svgHost).then((source) => current && drawn.set(source ?? undefined), (error) => this.errors.handleError(error));
226
+ });
227
+ return drawn;
228
+ }
152
229
  /**
153
230
  * `sfSymbol` and `drawable` say the same thing as `icon`, in the form most tabs want. A tab
154
231
  * names both to have an icon on both platforms, so the running platform's own comes first:
@@ -167,7 +244,7 @@ export class NativeTab {
167
244
  * host resolves it, which is the same thing an `<image src>` needs and the engine only does for
168
245
  * props it knows by name.
169
246
  */
170
- iconProps(icon) {
247
+ iconProps(icon, drawn) {
171
248
  if (!icon)
172
249
  return {};
173
250
  if ('sfSymbol' in icon)
@@ -176,6 +253,13 @@ export class NativeTab {
176
253
  return { iconType: 'xcasset', iconResourceName: icon.xcasset };
177
254
  if ('drawable' in icon)
178
255
  return { drawableIconResourceName: icon.drawable };
256
+ if ('svg' in icon) {
257
+ if (drawn === undefined)
258
+ return {};
259
+ return nativePlatform() === 'android'
260
+ ? { imageIconResource: drawn }
261
+ : { iconType: 'template', iconImageSource: drawn };
262
+ }
179
263
  const source = this.engine.resolveAsset('image' in icon ? icon.image : icon.template);
180
264
  return nativePlatform() === 'android'
181
265
  ? { imageIconResource: source }
@@ -218,7 +302,7 @@ export class NativeTab {
218
302
  };
219
303
  }
220
304
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: NativeTab, deps: [], target: i0.ɵɵFactoryTarget.Component });
221
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.2.0", type: NativeTab, isStandalone: true, selector: "native-tab", inputs: { path: { classPropertyName: "path", publicName: "path", isSignal: true, isRequired: true, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, sfSymbol: { classPropertyName: "sfSymbol", publicName: "sfSymbol", isSignal: true, isRequired: false, transformFunction: null }, drawable: { classPropertyName: "drawable", publicName: "drawable", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, selectedIcon: { classPropertyName: "selectedIcon", publicName: "selectedIcon", isSignal: true, isRequired: false, transformFunction: null }, systemItem: { classPropertyName: "systemItem", publicName: "systemItem", isSignal: true, isRequired: false, transformFunction: null }, badge: { classPropertyName: "badge", publicName: "badge", isSignal: true, isRequired: false, transformFunction: null }, accessibilityLabel: { classPropertyName: "accessibilityLabel", publicName: "accessibilityLabel", isSignal: true, isRequired: false, transformFunction: null }, standardAppearance: { classPropertyName: "standardAppearance", publicName: "standardAppearance", isSignal: true, isRequired: false, transformFunction: null }, scrollEdgeAppearance: { classPropertyName: "scrollEdgeAppearance", publicName: "scrollEdgeAppearance", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "screenKey": "screenKey()", "title": "title()", "badgeValue": "badge()", "tabBarItemAccessibilityLabel": "accessibilityLabel()", "systemItem": "systemItem()", "isTitleUndefined": "title() === undefined", "iconType": "icons().iconType", "iconResourceName": "icons().iconResourceName", "iconImageSource": "icons().iconImageSource", "selectedIconResourceName": "icons().selectedIconResourceName", "selectedIconImageSource": "icons().selectedIconImageSource", "drawableIconResourceName": "icons().drawableIconResourceName", "imageIconResource": "icons().imageIconResource", "selectedDrawableIconResourceName": "icons().selectedDrawableIconResourceName", "selectedImageIconResource": "icons().selectedImageIconResource", "standardAppearance": "standard()", "scrollEdgeAppearance": "scrollEdge()" } }, ngImport: i0, template: '', isInline: true });
305
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.2.0", type: NativeTab, isStandalone: true, selector: "native-tab", inputs: { path: { classPropertyName: "path", publicName: "path", isSignal: true, isRequired: true, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, sfSymbol: { classPropertyName: "sfSymbol", publicName: "sfSymbol", isSignal: true, isRequired: false, transformFunction: null }, drawable: { classPropertyName: "drawable", publicName: "drawable", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, selectedIcon: { classPropertyName: "selectedIcon", publicName: "selectedIcon", isSignal: true, isRequired: false, transformFunction: null }, systemItem: { classPropertyName: "systemItem", publicName: "systemItem", isSignal: true, isRequired: false, transformFunction: null }, role: { classPropertyName: "role", publicName: "role", isSignal: true, isRequired: false, transformFunction: null }, automaticallyActivatesSearch: { classPropertyName: "automaticallyActivatesSearch", publicName: "automaticallyActivatesSearch", isSignal: true, isRequired: false, transformFunction: null }, badge: { classPropertyName: "badge", publicName: "badge", isSignal: true, isRequired: false, transformFunction: null }, accessibilityLabel: { classPropertyName: "accessibilityLabel", publicName: "accessibilityLabel", isSignal: true, isRequired: false, transformFunction: null }, standardAppearance: { classPropertyName: "standardAppearance", publicName: "standardAppearance", isSignal: true, isRequired: false, transformFunction: null }, scrollEdgeAppearance: { classPropertyName: "scrollEdgeAppearance", publicName: "scrollEdgeAppearance", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "screenKey": "screenKey()", "title": "title()", "badgeValue": "badge()", "tabBarItemAccessibilityLabel": "accessibilityLabel()", "systemItem": "systemItem()", "tabRole": "role()", "automaticallyActivatesSearch": "automaticallyActivatesSearch()", "isTitleUndefined": "title() === undefined", "iconType": "icons().iconType", "iconResourceName": "icons().iconResourceName", "iconImageSource": "icons().iconImageSource", "selectedIconResourceName": "icons().selectedIconResourceName", "selectedIconImageSource": "icons().selectedIconImageSource", "drawableIconResourceName": "icons().drawableIconResourceName", "imageIconResource": "icons().imageIconResource", "selectedDrawableIconResourceName": "icons().selectedDrawableIconResourceName", "selectedImageIconResource": "icons().selectedImageIconResource", "standardAppearance": "standard()", "scrollEdgeAppearance": "scrollEdge()" } }, ngImport: i0, template: '', isInline: true });
222
306
  }
223
307
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: NativeTab, decorators: [{
224
308
  type: Component,
@@ -231,6 +315,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImpor
231
315
  '[badgeValue]': 'badge()',
232
316
  '[tabBarItemAccessibilityLabel]': 'accessibilityLabel()',
233
317
  '[systemItem]': 'systemItem()',
318
+ '[tabRole]': 'role()',
319
+ '[automaticallyActivatesSearch]': 'automaticallyActivatesSearch()',
234
320
  // `isTitleUndefined` is what `TabsScreen.ios.tsx` derives from the title, and it is not a
235
321
  // synonym for "no title": with a title set and this left at its default, the item renders as
236
322
  // an icon with a gap where the label belongs.
@@ -248,5 +334,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImpor
248
334
  '[scrollEdgeAppearance]': 'scrollEdge()',
249
335
  },
250
336
  }]
251
- }], ctorParameters: () => [], propDecorators: { path: [{ type: i0.Input, args: [{ isSignal: true, alias: "path", required: true }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], sfSymbol: [{ type: i0.Input, args: [{ isSignal: true, alias: "sfSymbol", required: false }] }], drawable: [{ type: i0.Input, args: [{ isSignal: true, alias: "drawable", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], selectedIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedIcon", required: false }] }], systemItem: [{ type: i0.Input, args: [{ isSignal: true, alias: "systemItem", required: false }] }], badge: [{ type: i0.Input, args: [{ isSignal: true, alias: "badge", required: false }] }], accessibilityLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "accessibilityLabel", required: false }] }], standardAppearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "standardAppearance", required: false }] }], scrollEdgeAppearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollEdgeAppearance", required: false }] }] } });
337
+ }], ctorParameters: () => [], propDecorators: { path: [{ type: i0.Input, args: [{ isSignal: true, alias: "path", required: true }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], sfSymbol: [{ type: i0.Input, args: [{ isSignal: true, alias: "sfSymbol", required: false }] }], drawable: [{ type: i0.Input, args: [{ isSignal: true, alias: "drawable", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], selectedIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedIcon", required: false }] }], systemItem: [{ type: i0.Input, args: [{ isSignal: true, alias: "systemItem", required: false }] }], role: [{ type: i0.Input, args: [{ isSignal: true, alias: "role", required: false }] }], automaticallyActivatesSearch: [{ type: i0.Input, args: [{ isSignal: true, alias: "automaticallyActivatesSearch", required: false }] }], badge: [{ type: i0.Input, args: [{ isSignal: true, alias: "badge", required: false }] }], accessibilityLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "accessibilityLabel", required: false }] }], standardAppearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "standardAppearance", required: false }] }], scrollEdgeAppearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollEdgeAppearance", required: false }] }] } });
252
338
  //# sourceMappingURL=native-tab.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"native-tab.js","sourceRoot":"","sources":["../src/native-tab.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * One item in a native tab bar, and the screen behind it.\n *\n * Declared in the tabs outlet's template, the way a `<native-header>` is declared in a page's:\n *\n * ```html\n * <native-tabs-outlet>\n * <native-tab path=\"library\" title=\"Library\" sfSymbol=\"books.vertical.fill\" />\n * <native-tab path=\"inbox\" title=\"Inbox\" sfSymbol=\"tray\" [badge]=\"unread()\" />\n * </native-tabs-outlet>\n * ```\n *\n * `path` names the child route this tab selects, the same way a `routerLink` names one, and is\n * what native identifies the tab by. Everything else is an ordinary input, which is the point of\n * declaring tabs here rather than in the route config: a badge is a count that changes, a title\n * may be translated, and a tab may not exist at all until a feature flag says so. Route data\n * cannot bind to any of that.\n *\n * **A bar item is not a view.** `UITabBarItem` is a model object - a title, an image and a badge\n * string - and UIKit has no way to put a view in its place; Android's bottom-navigation item is\n * the same. That is why this takes no content, while `<native-header-item>` does: a navigation\n * bar really does hold subviews. What customisation exists is here as inputs: images instead of\n * symbols, a different icon when selected, and the per-state appearance below.\n *\n * The host element *is* the `RNSTabsScreen`. The outlet keeps the screen mounted and creates\n * each activated route's component on a child view, so destroying a route cannot remove this\n * tab item from the bar.\n */\nimport { Component, ElementRef, computed, inject, input, type OnInit } from '@angular/core';\nimport { nativePlatform, type EngineNode, HostEngine } from '@ng-native/fabric';\nimport { NATIVE_TAB_DEFAULTS } from './native-bar-defaults.ts';\nimport { ownHost } from './own-host.ts';\n\n/**\n * Where a tab's picture comes from.\n *\n * `template` is the one worth knowing about: the image is drawn as a mask and tinted to the bar's\n * colour, so it follows selection the way a symbol does. `image` renders as authored, which is\n * what a multi-colour logo wants and what a monochrome icon almost never does.\n */\nexport type TabIcon =\n | { readonly sfSymbol: string }\n /** An image in the iOS asset catalogue, by name. */\n | { readonly xcasset: string }\n /** A `require()`d asset or a `{ uri }`, drawn as authored. */\n | { readonly image: unknown }\n /** The same, drawn as a mask and tinted. */\n | { readonly template: unknown }\n /** Android: a drawable resource name. */\n | { readonly drawable: string };\n\n/** One of Apple's standard items, which brings its own icon and title. */\nexport type TabSystemItem =\n | 'none'\n | 'bookmarks'\n | 'contacts'\n | 'downloads'\n | 'favorites'\n | 'featured'\n | 'history'\n | 'more'\n | 'mostRecent'\n | 'mostViewed'\n | 'recents'\n | 'search'\n | 'topRated';\n\nexport type TabBarBlurEffect =\n | 'none'\n | 'systemDefault'\n | 'extraLight'\n | 'light'\n | 'dark'\n | 'regular'\n | 'prominent'\n | 'systemUltraThinMaterial'\n | 'systemThinMaterial'\n | 'systemMaterial'\n | 'systemThickMaterial'\n | 'systemChromeMaterial';\n\n/**\n * How an item looks in one state. The names are the native ones, spelled as the codegen spec\n * spells them, so this forwards rather than translates and Apple's own documentation still reads\n * across.\n */\nexport interface TabStateAppearance {\n tabBarItemTitleFontFamily?: string;\n tabBarItemTitleFontSize?: number;\n tabBarItemTitleFontWeight?: string | number;\n tabBarItemTitleFontStyle?: string;\n tabBarItemTitleFontColor?: string | number;\n tabBarItemTitlePositionAdjustment?: { horizontal?: number; vertical?: number };\n tabBarItemIconColor?: string | number;\n tabBarItemBadgeBackgroundColor?: string | number;\n}\n\n/** The four states an item can be in. Anything absent falls back to the platform's own. */\nexport interface TabItemAppearance {\n normal?: TabStateAppearance;\n selected?: TabStateAppearance;\n focused?: TabStateAppearance;\n disabled?: TabStateAppearance;\n}\n\n/**\n * How the bar looks, per layout. `stacked` is the usual icon-over-title item; `inline` and\n * `compactInline` are the side-by-side layouts iPad and a compact height use.\n */\nexport interface TabAppearance {\n stacked?: TabItemAppearance;\n inline?: TabItemAppearance;\n compactInline?: TabItemAppearance;\n tabBarBackgroundColor?: string | number;\n tabBarShadowColor?: string | number;\n tabBarBlurEffect?: TabBarBlurEffect;\n}\n\n/** One icon, as the props native reads it through. */\nexport interface TabIconProps {\n iconType?: string;\n iconResourceName?: string;\n iconImageSource?: unknown;\n drawableIconResourceName?: string;\n imageIconResource?: unknown;\n}\n\n/**\n * An icon and its selected counterpart, as one set of props.\n *\n * Native carries a single `iconType` for both states, so the two have to be the same kind of\n * thing: a template image cannot become a symbol when selected. React Native Screens throws on\n * that pairing and so does this, because the alternative - what happened before this checked - is\n * that native quietly keeps the unselected icon and nothing anywhere says why.\n */\nexport function tabIconProps(\n icon: TabIconProps,\n selectedIcon: TabIconProps,\n): TabIconProps & {\n selectedIconResourceName?: string;\n selectedIconImageSource?: unknown;\n selectedDrawableIconResourceName?: string;\n selectedImageIconResource?: unknown;\n} {\n const hasBase = icon.iconType !== undefined || icon.drawableIconResourceName !== undefined;\n const hasSelected =\n selectedIcon.iconType !== undefined || selectedIcon.drawableIconResourceName !== undefined;\n\n if (hasSelected && !hasBase) {\n throw new Error('[angular-native] <native-tab> has a selectedIcon but no icon to select from');\n }\n if (hasSelected && icon.iconType !== selectedIcon.iconType) {\n throw new Error(\n `[angular-native] <native-tab> icon and selectedIcon must be the same kind: native has ` +\n `one iconType for both, so '${icon.iconType}' and '${selectedIcon.iconType}' cannot pair.`,\n );\n }\n\n return {\n ...icon,\n selectedIconResourceName: selectedIcon.iconResourceName,\n selectedIconImageSource: selectedIcon.iconImageSource,\n selectedDrawableIconResourceName: selectedIcon.drawableIconResourceName,\n selectedImageIconResource: selectedIcon.imageIconResource,\n };\n}\n\ndeclare const ngDevMode: unknown;\n\n/** The paths already warned about, so a tab bar rebuilt by a navigation says it once. */\nconst warnedTabs = new Set<string>();\n\n/**\n * Says, once per path and in development only, that a tab has no icon on the platform running it\n * because it names only the other platform's shorthand. A tab written against the iOS simulator\n * with `sfSymbol` shows a title and nothing above it on Android.\n */\nfunction warnIfIconOnOnePlatform(tab: NativeTab): void {\n if (typeof ngDevMode !== 'undefined' && !ngDevMode) return;\n if (tab.icon() || tab.systemItem()) return;\n const [own, other, missing, platform] =\n nativePlatform() === 'android'\n ? [tab.drawable(), tab.sfSymbol(), 'drawable', 'Android']\n : nativePlatform() === 'ios'\n ? [tab.sfSymbol(), tab.drawable(), 'sfSymbol', 'iOS']\n : [];\n if (own || !other) return;\n const path = tab.path();\n if (warnedTabs.has(path)) return;\n warnedTabs.add(path);\n console.warn(\n `[angular-native] <native-tab path=\"${path}\"> has no icon on ${platform}: ${platform} reads ` +\n `only ${missing}. Add ${missing}=\"...\", or bind [icon] for a tab meant for one platform.`,\n );\n}\n\n/**\n * The key native identifies a tab by: its path, or `/` for the tab at path `''`, since iOS reads an\n * empty key as none and fails on the tap that selects it. No route path starts with `/`.\n */\nexport function screenKeyOf(path: string): string {\n return path || '/';\n}\n\n@Component({\n selector: 'native-tab',\n template: '',\n host: {\n '[screenKey]': 'screenKey()',\n '[title]': 'title()',\n '[badgeValue]': 'badge()',\n '[tabBarItemAccessibilityLabel]': 'accessibilityLabel()',\n '[systemItem]': 'systemItem()',\n // `isTitleUndefined` is what `TabsScreen.ios.tsx` derives from the title, and it is not a\n // synonym for \"no title\": with a title set and this left at its default, the item renders as\n // an icon with a gap where the label belongs.\n '[isTitleUndefined]': 'title() === undefined',\n '[iconType]': 'icons().iconType',\n '[iconResourceName]': 'icons().iconResourceName',\n '[iconImageSource]': 'icons().iconImageSource',\n '[selectedIconResourceName]': 'icons().selectedIconResourceName',\n '[selectedIconImageSource]': 'icons().selectedIconImageSource',\n '[drawableIconResourceName]': 'icons().drawableIconResourceName',\n '[imageIconResource]': 'icons().imageIconResource',\n '[selectedDrawableIconResourceName]': 'icons().selectedDrawableIconResourceName',\n '[selectedImageIconResource]': 'icons().selectedImageIconResource',\n '[standardAppearance]': 'standard()',\n '[scrollEdgeAppearance]': 'scrollEdge()',\n },\n})\nexport class NativeTab implements OnInit {\n private readonly engine = inject(HostEngine);\n /** The app's `withTabDefaults`, for an appearance this tab does not bind itself. */\n private readonly defaults = inject(NATIVE_TAB_DEFAULTS);\n\n /**\n * The `RNSTabsScreen` this tab is. The outlet creates the activated route's component inside\n * it, which is why it is public: a tab is a slot as well as a bar item.\n */\n readonly screen: EngineNode;\n\n constructor() {\n this.screen = ownHost(inject(ElementRef).nativeElement as EngineNode, this.constructor);\n }\n\n /**\n * The child route this tab selects, as a path segment: `library` for `/tabs/library`. Native\n * identifies the tab by it too, so it must be unique in the bar.\n */\n readonly path = input.required<string>();\n\n protected readonly screenKey = computed(() => screenKeyOf(this.path()));\n\n /** The label under the icon. Absent gives an icon-only item. */\n readonly title = input<string>();\n\n /** iOS: an SF Symbol name, e.g. `books.vertical.fill`. Shorthand for `[icon]`. */\n readonly sfSymbol = input<string>();\n\n /** Android: a drawable resource name. Shorthand for `[icon]`. */\n readonly drawable = input<string>();\n\n /** Anything a symbol name cannot say: an image, a template image, an asset-catalogue name. */\n readonly icon = input<TabIcon>();\n\n /** Shown while this tab is the selected one. Absent leaves the platform to tint `icon`. */\n readonly selectedIcon = input<TabIcon>();\n\n /** One of Apple's standard items, which supplies its own icon and title. */\n readonly systemItem = input<TabSystemItem>();\n\n /** The badge on the item: a count, or a single character for a dot. */\n readonly badge = input<string>();\n\n /** Announced instead of the title. */\n readonly accessibilityLabel = input<string>();\n\n /** How the item looks. Colours are processed here, because native never sees the object's keys. */\n readonly standardAppearance = input<TabAppearance>();\n\n /** The same, for when the scroll view behind the bar is at its edge. */\n readonly scrollEdgeAppearance = input<TabAppearance>();\n\n ngOnInit(): void {\n warnIfIconOnOnePlatform(this);\n }\n\n protected readonly standard = computed(() =>\n this.processAppearance(this.standardAppearance() ?? this.defaults().standardAppearance),\n );\n protected readonly scrollEdge = computed(() =>\n this.processAppearance(this.scrollEdgeAppearance() ?? this.defaults().scrollEdgeAppearance),\n );\n\n protected readonly icons = computed(() =>\n tabIconProps(\n this.iconProps(this.icon() ?? this.shorthand()),\n this.iconProps(this.selectedIcon()),\n ),\n );\n\n /**\n * `sfSymbol` and `drawable` say the same thing as `icon`, in the form most tabs want. A tab\n * names both to have an icon on both platforms, so the running platform's own comes first:\n * Android reads only a drawable, and iOS only a symbol.\n */\n private shorthand(): TabIcon | undefined {\n const symbol = this.sfSymbol();\n const drawable = this.drawable();\n const icons = [symbol && { sfSymbol: symbol }, drawable && { drawable }];\n if (nativePlatform() === 'android') icons.reverse();\n return icons.find((icon) => icon) || undefined;\n }\n\n /**\n * One icon, as the four props native reads it through. A `require()`d asset is an id until the\n * host resolves it, which is the same thing an `<image src>` needs and the engine only does for\n * props it knows by name.\n */\n private iconProps(icon: TabIcon | undefined): TabIconProps {\n if (!icon) return {};\n if ('sfSymbol' in icon) return { iconType: 'sfSymbol', iconResourceName: icon.sfSymbol };\n if ('xcasset' in icon) return { iconType: 'xcasset', iconResourceName: icon.xcasset };\n if ('drawable' in icon) return { drawableIconResourceName: icon.drawable };\n\n const source = this.engine.resolveAsset('image' in icon ? icon.image : icon.template);\n return nativePlatform() === 'android'\n ? { imageIconResource: source }\n : { iconType: 'image' in icon ? 'image' : 'template', iconImageSource: source };\n }\n\n private processAppearance(appearance: TabAppearance | undefined): object | undefined {\n if (!appearance) return undefined;\n return {\n ...appearance,\n stacked: this.processItem(appearance.stacked),\n inline: this.processItem(appearance.inline),\n compactInline: this.processItem(appearance.compactInline),\n tabBarBackgroundColor: this.engine.color(appearance.tabBarBackgroundColor),\n tabBarShadowColor: this.engine.color(appearance.tabBarShadowColor),\n };\n }\n\n private processItem(item: TabItemAppearance | undefined): object | undefined {\n if (!item) return undefined;\n return {\n normal: this.processState(item.normal),\n selected: this.processState(item.selected),\n focused: this.processState(item.focused),\n disabled: this.processState(item.disabled),\n };\n }\n\n private processState(state: TabStateAppearance | undefined): object | undefined {\n if (!state) return undefined;\n return {\n ...state,\n // Native declares the weight as a string, and `600` is the natural way to write one.\n tabBarItemTitleFontWeight:\n state.tabBarItemTitleFontWeight === undefined\n ? undefined\n : String(state.tabBarItemTitleFontWeight),\n tabBarItemTitleFontColor: this.engine.color(state.tabBarItemTitleFontColor),\n tabBarItemIconColor: this.engine.color(state.tabBarItemIconColor),\n tabBarItemBadgeBackgroundColor: this.engine.color(state.tabBarItemBadgeBackgroundColor),\n };\n }\n}\n"]}
1
+ {"version":3,"file":"native-tab.js","sourceRoot":"","sources":["../src/native-tab.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,EACL,SAAS,EACT,UAAU,EACV,YAAY,EACZ,QAAQ,EACR,MAAM,EACN,MAAM,EACN,KAAK,EACL,MAAM,GAGP,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,cAAc,EAAmB,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAChF,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;;AA4GlE;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAC1B,IAAkB,EAClB,YAA0B;IAO1B,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,KAAK,SAAS,IAAI,IAAI,CAAC,wBAAwB,KAAK,SAAS,CAAC;IAC3F,MAAM,WAAW,GACf,YAAY,CAAC,QAAQ,KAAK,SAAS,IAAI,YAAY,CAAC,wBAAwB,KAAK,SAAS,CAAC;IAE7F,IAAI,WAAW,IAAI,CAAC,OAAO,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,6EAA6E,CAAC,CAAC;IACjG,CAAC;IACD,IAAI,WAAW,IAAI,IAAI,CAAC,QAAQ,KAAK,YAAY,CAAC,QAAQ,EAAE,CAAC;QAC3D,MAAM,IAAI,KAAK,CACb,wFAAwF;YACtF,8BAA8B,IAAI,CAAC,QAAQ,UAAU,YAAY,CAAC,QAAQ,gBAAgB,CAC7F,CAAC;IACJ,CAAC;IAED,OAAO;QACL,GAAG,IAAI;QACP,wBAAwB,EAAE,YAAY,CAAC,gBAAgB;QACvD,uBAAuB,EAAE,YAAY,CAAC,eAAe;QACrD,gCAAgC,EAAE,YAAY,CAAC,wBAAwB;QACvE,yBAAyB,EAAE,YAAY,CAAC,iBAAiB;KAC1D,CAAC;AACJ,CAAC;AAID,SAAS,KAAK,CAAC,IAAyB;IACtC,OAAO,IAAI,KAAK,SAAS,IAAI,KAAK,IAAI,IAAI,CAAC;AAC7C,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;AAErC;;;;GAIG;AACH,SAAS,uBAAuB,CAAC,GAAc;IAC7C,IAAI,OAAO,SAAS,KAAK,WAAW,IAAI,CAAC,SAAS;QAAE,OAAO;IAC3D,IAAI,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,UAAU,EAAE;QAAE,OAAO;IAC3C,MAAM,CAAC,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,GACnC,cAAc,EAAE,KAAK,SAAS;QAC5B,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,GAAG,CAAC,QAAQ,EAAE,EAAE,UAAU,EAAE,SAAS,CAAC;QACzD,CAAC,CAAC,cAAc,EAAE,KAAK,KAAK;YAC1B,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,GAAG,CAAC,QAAQ,EAAE,EAAE,UAAU,EAAE,KAAK,CAAC;YACrD,CAAC,CAAC,EAAE,CAAC;IACX,IAAI,GAAG,IAAI,CAAC,KAAK;QAAE,OAAO;IAC1B,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IACxB,IAAI,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO;IACjC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACrB,OAAO,CAAC,IAAI,CACV,sCAAsC,IAAI,qBAAqB,QAAQ,KAAK,QAAQ,SAAS;QAC3F,QAAQ,OAAO,SAAS,OAAO,0DAA0D,CAC5F,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,iBAAiB,CAAC,IAAY;IACrC,IAAI,OAAO,SAAS,KAAK,WAAW,IAAI,CAAC,SAAS;QAAE,OAAO;IAC3D,IAAI,UAAU,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC;QAAE,OAAO;IAC1C,UAAU,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;IAC9B,OAAO,CAAC,IAAI,CACV,sCAAsC,IAAI,mDAAmD;QAC3F,uFAAuF,CAC1F,CAAC;AACJ,CAAC;AAED,+FAA+F;AAC/F,SAAS,WAAW;IAClB,OAAO,cAAc,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAClD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,IAAI,IAAI,GAAG,CAAC;AACrB,CAAC;AA8BD,MAAM,OAAO,SAAS;IACH,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;IAC7C,oFAAoF;IACnE,QAAQ,GAAG,MAAM,CAAC,mBAAmB,CAAC,CAAC;IACvC,WAAW,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC;IACxC,OAAO,GAAG,MAAM,CAAC,aAAa,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;IAE/C;;;OAGG;IACM,MAAM,CAAa;IAE5B;QACE,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,aAA2B,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IAC1F,CAAC;IAED;;;OAGG;IACM,IAAI,GAAG,KAAK,CAAC,QAAQ;6EAAU,CAAC;IAEtB,SAAS,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;kFAAC,CAAC;IAExE,gEAAgE;IACvD,KAAK,GAAG,KAAK;yFAAU,CAAC;IAEjC,kFAAkF;IACzE,QAAQ,GAAG,KAAK;4FAAU,CAAC;IAEpC,iEAAiE;IACxD,QAAQ,GAAG,KAAK;4FAAU,CAAC;IAEpC,8FAA8F;IACrF,IAAI,GAAG,KAAK;wFAAW,CAAC;IAEjC,2FAA2F;IAClF,YAAY,GAAG,KAAK;gGAAW,CAAC;IAEzC,4EAA4E;IACnE,UAAU,GAAG,KAAK;8FAAiB,CAAC;IAE7C;;;;;;;;OAQG;IACM,IAAI,GAAG,KAAK;wFAAW,CAAC;IAEjC;;;;;OAKG;IACM,4BAA4B,GAAG,KAAK,CAAC,SAAS,oGAAI,SAAS,EAAE,eAAe,GAAG,CAAC;IAEzF,uEAAuE;IAC9D,KAAK,GAAG,KAAK;yFAAU,CAAC;IAEjC,sCAAsC;IAC7B,kBAAkB,GAAG,KAAK;sGAAU,CAAC;IAE9C,mGAAmG;IAC1F,kBAAkB,GAAG,KAAK;sGAAiB,CAAC;IAErD,wEAAwE;IAC/D,oBAAoB,GAAG,KAAK;wGAAiB,CAAC;IAEvD,QAAQ;QACN,uBAAuB,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAEkB,QAAQ,GAAG,QAAQ,CAAC,GAAG,EAAE,CAC1C,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,kBAAkB,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC,kBAAkB,CAAC;iFACxF,CAAC;IACiB,UAAU,GAAG,QAAQ,CAAC,GAAG,EAAE,CAC5C,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,oBAAoB,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC,oBAAoB,CAAC;mFAC5F,CAAC;IAEF,6DAA6D;IAC5C,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClC,iBAAiB,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAEhD,KAAK,GAAG,QAAQ,CAAC,GAAG,EAAE;QACvC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC,SAAS,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;QAC/E,4FAA4F;QAC5F,0BAA0B;QAC1B,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,IAAI,CAAC,SAAS,EAAE,KAAK,SAAS,CAAC;QACrE,MAAM,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,IAAI,CAAC,iBAAiB,EAAE,CAAC,CAAC;QAC9F,OAAO,YAAY,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACtC,CAAC;8EAAC,CAAC;IAEH;;;OAGG;IACK,KAAK,CAAC,IAAiC;QAC7C,MAAM,MAAM,GAAG,QAAQ,CACrB,GAAG,EAAE;YACH,MAAM,GAAG,GAAG,IAAI,EAAE,CAAC;YACnB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QACpF,CAAC,8EACC,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,EAAE,IAAI,KAAK,CAAC,EAAE,IAAI,GAC5D,CAAC;QACF,MAAM,KAAK,GAAG,MAAM,CAAU,SAAS;kFAAC,CAAC;QACzC,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE;YACnB,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;YACxB,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YACrB,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO;gBAAE,OAAO;YACrC,IAAI,CAAC,IAAI,CAAC,WAAW;gBAAE,OAAO,iBAAiB,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YAC7D,IAAI,OAAO,GAAG,IAAI,CAAC;YACnB,SAAS,CAAC,GAAG,EAAE,CAAC,CAAC,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC;YACnC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CACjE,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,MAAM,IAAI,SAAS,CAAC,EACrD,CAAC,KAAc,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CACnD,CAAC;QACJ,CAAC,CAAC,CAAC;QACH,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;OAIG;IACK,SAAS;QACf,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,CAAC,MAAM,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,QAAQ,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QACzE,IAAI,cAAc,EAAE,KAAK,SAAS;YAAE,KAAK,CAAC,OAAO,EAAE,CAAC;QACpD,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC;IACjD,CAAC;IAED;;;;OAIG;IACK,SAAS,CAAC,IAAyB,EAAE,KAAc;QACzD,IAAI,CAAC,IAAI;YAAE,OAAO,EAAE,CAAC;QACrB,IAAI,UAAU,IAAI,IAAI;YAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,gBAAgB,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;QACzF,IAAI,SAAS,IAAI,IAAI;YAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,gBAAgB,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC;QACtF,IAAI,UAAU,IAAI,IAAI;YAAE,OAAO,EAAE,wBAAwB,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC3E,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;YAClB,IAAI,KAAK,KAAK,SAAS;gBAAE,OAAO,EAAE,CAAC;YACnC,OAAO,cAAc,EAAE,KAAK,SAAS;gBACnC,CAAC,CAAC,EAAE,iBAAiB,EAAE,KAAK,EAAE;gBAC9B,CAAC,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC;QACvD,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACtF,OAAO,cAAc,EAAE,KAAK,SAAS;YACnC,CAAC,CAAC,EAAE,iBAAiB,EAAE,MAAM,EAAE;YAC/B,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,EAAE,eAAe,EAAE,MAAM,EAAE,CAAC;IACpF,CAAC;IAEO,iBAAiB,CAAC,UAAqC;QAC7D,IAAI,CAAC,UAAU;YAAE,OAAO,SAAS,CAAC;QAClC,OAAO;YACL,GAAG,UAAU;YACb,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC;YAC7C,MAAM,EAAE,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,MAAM,CAAC;YAC3C,aAAa,EAAE,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,aAAa,CAAC;YACzD,qBAAqB,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,qBAAqB,CAAC;YAC1E,iBAAiB,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,iBAAiB,CAAC;SACnE,CAAC;IACJ,CAAC;IAEO,WAAW,CAAC,IAAmC;QACrD,IAAI,CAAC,IAAI;YAAE,OAAO,SAAS,CAAC;QAC5B,OAAO;YACL,MAAM,EAAE,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC;YACtC,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC;YAC1C,OAAO,EAAE,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC;YACxC,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC;SAC3C,CAAC;IACJ,CAAC;IAEO,YAAY,CAAC,KAAqC;QACxD,IAAI,CAAC,KAAK;YAAE,OAAO,SAAS,CAAC;QAC7B,OAAO;YACL,GAAG,KAAK;YACR,qFAAqF;YACrF,yBAAyB,EACvB,KAAK,CAAC,yBAAyB,KAAK,SAAS;gBAC3C,CAAC,CAAC,SAAS;gBACX,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,yBAAyB,CAAC;YAC7C,wBAAwB,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,wBAAwB,CAAC;YAC3E,mBAAmB,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,mBAAmB,CAAC;YACjE,8BAA8B,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,8BAA8B,CAAC;SACxF,CAAC;IACJ,CAAC;uGAtMU,SAAS;2FAAT,SAAS,+tFA1BV,EAAE;;2FA0BD,SAAS;kBA5BrB,SAAS;mBAAC;oBACT,QAAQ,EAAE,YAAY;oBACtB,QAAQ,EAAE,EAAE;oBACZ,IAAI,EAAE;wBACJ,aAAa,EAAE,aAAa;wBAC5B,SAAS,EAAE,SAAS;wBACpB,cAAc,EAAE,SAAS;wBACzB,gCAAgC,EAAE,sBAAsB;wBACxD,cAAc,EAAE,cAAc;wBAC9B,WAAW,EAAE,QAAQ;wBACrB,gCAAgC,EAAE,gCAAgC;wBAClE,0FAA0F;wBAC1F,6FAA6F;wBAC7F,8CAA8C;wBAC9C,oBAAoB,EAAE,uBAAuB;wBAC7C,YAAY,EAAE,kBAAkB;wBAChC,oBAAoB,EAAE,0BAA0B;wBAChD,mBAAmB,EAAE,yBAAyB;wBAC9C,4BAA4B,EAAE,kCAAkC;wBAChE,2BAA2B,EAAE,iCAAiC;wBAC9D,4BAA4B,EAAE,kCAAkC;wBAChE,qBAAqB,EAAE,2BAA2B;wBAClD,oCAAoC,EAAE,0CAA0C;wBAChF,6BAA6B,EAAE,mCAAmC;wBAClE,sBAAsB,EAAE,YAAY;wBACpC,wBAAwB,EAAE,cAAc;qBACzC;iBACF","sourcesContent":["/**\n * One item in a native tab bar, and the screen behind it.\n *\n * Declared in the tabs outlet's template, the way a `<native-header>` is declared in a page's:\n *\n * ```html\n * <native-tabs-outlet>\n * <native-tab path=\"library\" title=\"Library\" sfSymbol=\"books.vertical.fill\" />\n * <native-tab path=\"inbox\" title=\"Inbox\" sfSymbol=\"tray\" [badge]=\"unread()\" />\n * </native-tabs-outlet>\n * ```\n *\n * `path` names the child route this tab selects, the same way a `routerLink` names one, and is\n * what native identifies the tab by. Everything else is an ordinary input, which is the point of\n * declaring tabs here rather than in the route config: a badge is a count that changes, a title\n * may be translated, and a tab may not exist at all until a feature flag says so. Route data\n * cannot bind to any of that.\n *\n * **A bar item is not a view.** `UITabBarItem` is a model object - a title, an image and a badge\n * string - and UIKit has no way to put a view in its place; Android's bottom-navigation item is\n * the same. That is why this takes no content, while `<native-header-item>` does: a navigation\n * bar really does hold subviews. What customisation exists is here as inputs: images instead of\n * symbols, a different icon when selected, and the per-state appearance below.\n *\n * The host element *is* the `RNSTabsScreen`. The outlet keeps the screen mounted and creates\n * each activated route's component on a child view, so destroying a route cannot remove this\n * tab item from the bar.\n */\nimport {\n Component,\n ElementRef,\n ErrorHandler,\n computed,\n effect,\n inject,\n input,\n signal,\n type OnInit,\n type Signal,\n} from '@angular/core';\nimport { nativePlatform, type EngineNode, HostEngine } from '@ng-native/fabric';\nimport { NATIVE_TAB_DEFAULTS } from './native-bar-defaults.ts';\nimport { ownHost } from './own-host.ts';\nimport { optionalBoolean } from './transforms.ts';\nimport { SVG_ICON_HOST, SVG_ICON_RENDERER } from './svg-icons.ts';\n\n/**\n * Where a tab's picture comes from.\n *\n * `template` is the one worth knowing about: the image is drawn as a mask and tinted to the bar's\n * colour, so it follows selection the way a symbol does. `image` renders as authored, which is\n * what a multi-colour logo wants and what a monochrome icon almost never does.\n */\nexport type TabIcon =\n | { readonly sfSymbol: string }\n /** An image in the iOS asset catalogue, by name. */\n | { readonly xcasset: string }\n /** A `require()`d asset or a `{ uri }`, drawn as authored. */\n | { readonly image: unknown }\n /** The same, drawn as a mask and tinted. */\n | { readonly template: unknown }\n /** Android: a drawable resource name. */\n | { readonly drawable: string }\n /**\n * SVG markup, such as an `@ng-icons` icon, drawn as a mask and tinted like `template`. Native\n * cannot draw SVG, so it is drawn once by the renderer `withSvgIcons` provides, `size` points\n * square: 25 on iOS and 24 on Android unless it says. The tab has no icon until it has been.\n */\n | { readonly svg: string; readonly size?: number };\n\n/** One of Apple's standard items, which brings its own icon and title. */\nexport type TabSystemItem =\n | 'none'\n | 'bookmarks'\n | 'contacts'\n | 'downloads'\n | 'favorites'\n | 'featured'\n | 'history'\n | 'more'\n | 'mostRecent'\n | 'mostViewed'\n | 'recents'\n | 'search'\n | 'topRated';\n\n/**\n * iOS: what a tab is for, beyond its bar item. `search` makes it a `UISearchTab`, which iOS 26\n * draws apart from the others: a circular button at the trailing end of the bar.\n */\nexport type TabRole = 'none' | 'search';\n\nexport type TabBarBlurEffect =\n | 'none'\n | 'systemDefault'\n | 'extraLight'\n | 'light'\n | 'dark'\n | 'regular'\n | 'prominent'\n | 'systemUltraThinMaterial'\n | 'systemThinMaterial'\n | 'systemMaterial'\n | 'systemThickMaterial'\n | 'systemChromeMaterial';\n\n/**\n * How an item looks in one state. The names are the native ones, spelled as the codegen spec\n * spells them, so this forwards rather than translates and Apple's own documentation still reads\n * across.\n */\nexport interface TabStateAppearance {\n tabBarItemTitleFontFamily?: string;\n tabBarItemTitleFontSize?: number;\n tabBarItemTitleFontWeight?: string | number;\n tabBarItemTitleFontStyle?: string;\n tabBarItemTitleFontColor?: string | number;\n tabBarItemTitlePositionAdjustment?: { horizontal?: number; vertical?: number };\n tabBarItemIconColor?: string | number;\n tabBarItemBadgeBackgroundColor?: string | number;\n}\n\n/** The four states an item can be in. Anything absent falls back to the platform's own. */\nexport interface TabItemAppearance {\n normal?: TabStateAppearance;\n selected?: TabStateAppearance;\n focused?: TabStateAppearance;\n disabled?: TabStateAppearance;\n}\n\n/**\n * How the bar looks, per layout. `stacked` is the usual icon-over-title item; `inline` and\n * `compactInline` are the side-by-side layouts iPad and a compact height use.\n */\nexport interface TabAppearance {\n stacked?: TabItemAppearance;\n inline?: TabItemAppearance;\n compactInline?: TabItemAppearance;\n tabBarBackgroundColor?: string | number;\n tabBarShadowColor?: string | number;\n tabBarBlurEffect?: TabBarBlurEffect;\n}\n\n/** One icon, as the props native reads it through. */\nexport interface TabIconProps {\n iconType?: string;\n iconResourceName?: string;\n iconImageSource?: unknown;\n drawableIconResourceName?: string;\n imageIconResource?: unknown;\n}\n\n/**\n * An icon and its selected counterpart, as one set of props.\n *\n * Native carries a single `iconType` for both states, so the two have to be the same kind of\n * thing: a template image cannot become a symbol when selected. React Native Screens throws on\n * that pairing and so does this, because the alternative - what happened before this checked - is\n * that native quietly keeps the unselected icon and nothing anywhere says why.\n */\nexport function tabIconProps(\n icon: TabIconProps,\n selectedIcon: TabIconProps,\n): TabIconProps & {\n selectedIconResourceName?: string;\n selectedIconImageSource?: unknown;\n selectedDrawableIconResourceName?: string;\n selectedImageIconResource?: unknown;\n} {\n const hasBase = icon.iconType !== undefined || icon.drawableIconResourceName !== undefined;\n const hasSelected =\n selectedIcon.iconType !== undefined || selectedIcon.drawableIconResourceName !== undefined;\n\n if (hasSelected && !hasBase) {\n throw new Error('[angular-native] <native-tab> has a selectedIcon but no icon to select from');\n }\n if (hasSelected && icon.iconType !== selectedIcon.iconType) {\n throw new Error(\n `[angular-native] <native-tab> icon and selectedIcon must be the same kind: native has ` +\n `one iconType for both, so '${icon.iconType}' and '${selectedIcon.iconType}' cannot pair.`,\n );\n }\n\n return {\n ...icon,\n selectedIconResourceName: selectedIcon.iconResourceName,\n selectedIconImageSource: selectedIcon.iconImageSource,\n selectedDrawableIconResourceName: selectedIcon.drawableIconResourceName,\n selectedImageIconResource: selectedIcon.imageIconResource,\n };\n}\n\ndeclare const ngDevMode: unknown;\n\nfunction isSvg(icon: TabIcon | undefined): icon is Extract<TabIcon, { svg: string }> {\n return icon !== undefined && 'svg' in icon;\n}\n\n/** The paths already warned about, so a tab bar rebuilt by a navigation says it once. */\nconst warnedTabs = new Set<string>();\n\n/**\n * Says, once per path and in development only, that a tab has no icon on the platform running it\n * because it names only the other platform's shorthand. A tab written against the iOS simulator\n * with `sfSymbol` shows a title and nothing above it on Android.\n */\nfunction warnIfIconOnOnePlatform(tab: NativeTab): void {\n if (typeof ngDevMode !== 'undefined' && !ngDevMode) return;\n if (tab.icon() || tab.systemItem()) return;\n const [own, other, missing, platform] =\n nativePlatform() === 'android'\n ? [tab.drawable(), tab.sfSymbol(), 'drawable', 'Android']\n : nativePlatform() === 'ios'\n ? [tab.sfSymbol(), tab.drawable(), 'sfSymbol', 'iOS']\n : [];\n if (own || !other) return;\n const path = tab.path();\n if (warnedTabs.has(path)) return;\n warnedTabs.add(path);\n console.warn(\n `[angular-native] <native-tab path=\"${path}\"> has no icon on ${platform}: ${platform} reads ` +\n `only ${missing}. Add ${missing}=\"...\", or bind [icon] for a tab meant for one platform.`,\n );\n}\n\n/**\n * Says, once per path and in development only, that a tab's icon is SVG and the app gave nothing\n * to draw it, so the tab shows no icon.\n */\nfunction warnNoSvgRenderer(path: string): void {\n if (typeof ngDevMode !== 'undefined' && !ngDevMode) return;\n if (warnedTabs.has(`svg:${path}`)) return;\n warnedTabs.add(`svg:${path}`);\n console.warn(\n `[angular-native] <native-tab path=\"${path}\"> has an SVG icon and nothing to draw it, so it ` +\n `shows none. Add withSvgIcons(SvgImages) from @ng-native/icons to provideNativeRouter.`,\n );\n}\n\n/** A tab bar icon's size in points when an SVG icon gives none, as each platform draws one. */\nfunction svgIconSize(): number {\n return nativePlatform() === 'android' ? 24 : 25;\n}\n\n/**\n * The key native identifies a tab by: its path, or `/` for the tab at path `''`, since iOS reads an\n * empty key as none and fails on the tap that selects it. No route path starts with `/`.\n */\nexport function screenKeyOf(path: string): string {\n return path || '/';\n}\n\n@Component({\n selector: 'native-tab',\n template: '',\n host: {\n '[screenKey]': 'screenKey()',\n '[title]': 'title()',\n '[badgeValue]': 'badge()',\n '[tabBarItemAccessibilityLabel]': 'accessibilityLabel()',\n '[systemItem]': 'systemItem()',\n '[tabRole]': 'role()',\n '[automaticallyActivatesSearch]': 'automaticallyActivatesSearch()',\n // `isTitleUndefined` is what `TabsScreen.ios.tsx` derives from the title, and it is not a\n // synonym for \"no title\": with a title set and this left at its default, the item renders as\n // an icon with a gap where the label belongs.\n '[isTitleUndefined]': 'title() === undefined',\n '[iconType]': 'icons().iconType',\n '[iconResourceName]': 'icons().iconResourceName',\n '[iconImageSource]': 'icons().iconImageSource',\n '[selectedIconResourceName]': 'icons().selectedIconResourceName',\n '[selectedIconImageSource]': 'icons().selectedIconImageSource',\n '[drawableIconResourceName]': 'icons().drawableIconResourceName',\n '[imageIconResource]': 'icons().imageIconResource',\n '[selectedDrawableIconResourceName]': 'icons().selectedDrawableIconResourceName',\n '[selectedImageIconResource]': 'icons().selectedImageIconResource',\n '[standardAppearance]': 'standard()',\n '[scrollEdgeAppearance]': 'scrollEdge()',\n },\n})\nexport class NativeTab implements OnInit {\n private readonly engine = inject(HostEngine);\n /** The app's `withTabDefaults`, for an appearance this tab does not bind itself. */\n private readonly defaults = inject(NATIVE_TAB_DEFAULTS);\n private readonly svgRenderer = inject(SVG_ICON_RENDERER);\n private readonly svgHost = inject(SVG_ICON_HOST, { optional: true });\n private readonly errors = inject(ErrorHandler);\n\n /**\n * The `RNSTabsScreen` this tab is. The outlet creates the activated route's component inside\n * it, which is why it is public: a tab is a slot as well as a bar item.\n */\n readonly screen: EngineNode;\n\n constructor() {\n this.screen = ownHost(inject(ElementRef).nativeElement as EngineNode, this.constructor);\n }\n\n /**\n * The child route this tab selects, as a path segment: `library` for `/tabs/library`. Native\n * identifies the tab by it too, so it must be unique in the bar.\n */\n readonly path = input.required<string>();\n\n protected readonly screenKey = computed(() => screenKeyOf(this.path()));\n\n /** The label under the icon. Absent gives an icon-only item. */\n readonly title = input<string>();\n\n /** iOS: an SF Symbol name, e.g. `books.vertical.fill`. Shorthand for `[icon]`. */\n readonly sfSymbol = input<string>();\n\n /** Android: a drawable resource name. Shorthand for `[icon]`. */\n readonly drawable = input<string>();\n\n /** Anything a symbol name cannot say: an image, a template image, an asset-catalogue name. */\n readonly icon = input<TabIcon>();\n\n /** Shown while this tab is the selected one. Absent leaves the platform to tint `icon`. */\n readonly selectedIcon = input<TabIcon>();\n\n /** One of Apple's standard items, which supplies its own icon and title. */\n readonly systemItem = input<TabSystemItem>();\n\n /**\n * iOS 26.1 and later: `search` makes this the bar's search tab, a `UISearchTab`, set apart at the\n * trailing end. Needs react-native-screens 4.29 or later; an older one ignores it.\n *\n * Native reads it once, when the tab is created, and asserts in a debug build if it changes\n * afterwards, so bind a value that does not. A bar has at most one search tab. Built with the\n * iOS 26 SDK, as Expo Go is, `systemItem=\"search\"` already gets the detached button; an app\n * built with the iOS 27 SDK needs this, and on iOS 27 `automaticallyActivatesSearch` as well.\n */\n readonly role = input<TabRole>();\n\n /**\n * iOS 26.1 and later, for a `role=\"search\"` tab: selecting the tab focuses the search field of\n * the `<native-search-bar>` in the header of the stack inside it, which that stack has to have.\n * On iOS 27 the tab is detached only while this is on, and the tab's first visit does not focus\n * the field. Expo Go's react-native-screens is older and ignores it.\n */\n readonly automaticallyActivatesSearch = input(undefined, { transform: optionalBoolean });\n\n /** The badge on the item: a count, or a single character for a dot. */\n readonly badge = input<string>();\n\n /** Announced instead of the title. */\n readonly accessibilityLabel = input<string>();\n\n /** How the item looks. Colours are processed here, because native never sees the object's keys. */\n readonly standardAppearance = input<TabAppearance>();\n\n /** The same, for when the scroll view behind the bar is at its edge. */\n readonly scrollEdgeAppearance = input<TabAppearance>();\n\n ngOnInit(): void {\n warnIfIconOnOnePlatform(this);\n }\n\n protected readonly standard = computed(() =>\n this.processAppearance(this.standardAppearance() ?? this.defaults().standardAppearance),\n );\n protected readonly scrollEdge = computed(() =>\n this.processAppearance(this.scrollEdgeAppearance() ?? this.defaults().scrollEdgeAppearance),\n );\n\n /** `icon` and `selectedIcon` as drawn, when they are SVG. */\n private readonly drawnIcon = this.drawn(this.icon);\n private readonly drawnSelectedIcon = this.drawn(this.selectedIcon);\n\n protected readonly icons = computed(() => {\n const icon = this.iconProps(this.icon() ?? this.shorthand(), this.drawnIcon());\n // A selected icon drawn before the icon it replaces waits for it: native takes no pair of a\n // selected icon and none.\n const undrawn = isSvg(this.icon()) && this.drawnIcon() === undefined;\n const selected = undrawn ? {} : this.iconProps(this.selectedIcon(), this.drawnSelectedIcon());\n return tabIconProps(icon, selected);\n });\n\n /**\n * An SVG icon as the renderer drew it, undefined until it has. Markup that changes is drawn\n * again, and a drawing that finishes after newer markup arrived is dropped.\n */\n private drawn(icon: Signal<TabIcon | undefined>): Signal<unknown> {\n const markup = computed(\n () => {\n const svg = icon();\n return isSvg(svg) ? { svg: svg.svg, size: svg.size ?? svgIconSize() } : undefined;\n },\n { equal: (a, b) => a?.svg === b?.svg && a?.size === b?.size },\n );\n const drawn = signal<unknown>(undefined);\n effect((onCleanup) => {\n const wanted = markup();\n drawn.set(undefined);\n if (!wanted || !this.svgHost) return;\n if (!this.svgRenderer) return warnNoSvgRenderer(this.path());\n let current = true;\n onCleanup(() => (current = false));\n this.svgRenderer.render(wanted.svg, wanted.size, this.svgHost).then(\n (source) => current && drawn.set(source ?? undefined),\n (error: unknown) => this.errors.handleError(error),\n );\n });\n return drawn;\n }\n\n /**\n * `sfSymbol` and `drawable` say the same thing as `icon`, in the form most tabs want. A tab\n * names both to have an icon on both platforms, so the running platform's own comes first:\n * Android reads only a drawable, and iOS only a symbol.\n */\n private shorthand(): TabIcon | undefined {\n const symbol = this.sfSymbol();\n const drawable = this.drawable();\n const icons = [symbol && { sfSymbol: symbol }, drawable && { drawable }];\n if (nativePlatform() === 'android') icons.reverse();\n return icons.find((icon) => icon) || undefined;\n }\n\n /**\n * One icon, as the four props native reads it through. A `require()`d asset is an id until the\n * host resolves it, which is the same thing an `<image src>` needs and the engine only does for\n * props it knows by name.\n */\n private iconProps(icon: TabIcon | undefined, drawn: unknown): TabIconProps {\n if (!icon) return {};\n if ('sfSymbol' in icon) return { iconType: 'sfSymbol', iconResourceName: icon.sfSymbol };\n if ('xcasset' in icon) return { iconType: 'xcasset', iconResourceName: icon.xcasset };\n if ('drawable' in icon) return { drawableIconResourceName: icon.drawable };\n if ('svg' in icon) {\n if (drawn === undefined) return {};\n return nativePlatform() === 'android'\n ? { imageIconResource: drawn }\n : { iconType: 'template', iconImageSource: drawn };\n }\n\n const source = this.engine.resolveAsset('image' in icon ? icon.image : icon.template);\n return nativePlatform() === 'android'\n ? { imageIconResource: source }\n : { iconType: 'image' in icon ? 'image' : 'template', iconImageSource: source };\n }\n\n private processAppearance(appearance: TabAppearance | undefined): object | undefined {\n if (!appearance) return undefined;\n return {\n ...appearance,\n stacked: this.processItem(appearance.stacked),\n inline: this.processItem(appearance.inline),\n compactInline: this.processItem(appearance.compactInline),\n tabBarBackgroundColor: this.engine.color(appearance.tabBarBackgroundColor),\n tabBarShadowColor: this.engine.color(appearance.tabBarShadowColor),\n };\n }\n\n private processItem(item: TabItemAppearance | undefined): object | undefined {\n if (!item) return undefined;\n return {\n normal: this.processState(item.normal),\n selected: this.processState(item.selected),\n focused: this.processState(item.focused),\n disabled: this.processState(item.disabled),\n };\n }\n\n private processState(state: TabStateAppearance | undefined): object | undefined {\n if (!state) return undefined;\n return {\n ...state,\n // Native declares the weight as a string, and `600` is the natural way to write one.\n tabBarItemTitleFontWeight:\n state.tabBarItemTitleFontWeight === undefined\n ? undefined\n : String(state.tabBarItemTitleFontWeight),\n tabBarItemTitleFontColor: this.engine.color(state.tabBarItemTitleFontColor),\n tabBarItemIconColor: this.engine.color(state.tabBarItemIconColor),\n tabBarItemBadgeBackgroundColor: this.engine.color(state.tabBarItemBadgeBackgroundColor),\n };\n }\n}\n"]}
@@ -151,6 +151,8 @@ export declare class NativeTabsOutlet implements RouterOutletContract, AfterCont
151
151
  * first screen, which a push into the tab from outside it shows first.
152
152
  */
153
153
  private unopenedTabOf;
154
+ /** Where the tab `url` is in was left, while a screen over the bar covers it. */
155
+ private coveredAt;
154
156
  /** Whether `url` is a tab other than the one in front, or a page inside one. */
155
157
  private behind;
156
158
  /**
@@ -25,7 +25,7 @@
25
25
  * was: its scroll offset, its text, and its own stack if it has one. That is the reuse strategy
26
26
  * detaching the tab's whole subtree rather than only its leaf.
27
27
  */
28
- import { ApplicationRef, ChangeDetectorRef, Component, DestroyRef, ElementRef, ErrorHandler, Injector, Renderer2, computed, contentChildren, createComponent, effect, inject, input, signal, untracked, } from '@angular/core';
28
+ import { ApplicationRef, ChangeDetectorRef, Component, DestroyRef, ElementRef, ErrorHandler, Injector, Renderer2, computed, contentChildren, createComponent, effect, inject, input, signal, untracked, ViewContainerRef, } from '@angular/core';
29
29
  import { SCREEN_IN_FRONT } from '@ng-native/device';
30
30
  import { ActivatedRoute, ChildrenOutletContexts, NavigationEnd, PRIMARY_OUTLET, Router, } from '@angular/router';
31
31
  import { bindRouteInputs } from "./bind-route-inputs.js";
@@ -37,6 +37,7 @@ import { ownHost } from "./own-host.js";
37
37
  import { markTabRoute } from "./tab-routes.js";
38
38
  import { IN_TAB } from "./in-tab.js";
39
39
  import { taken, withoutPresented } from "./presented-route.js";
40
+ import { SVG_ICON_HOST } from "./svg-icons.js";
40
41
  import * as i0 from "@angular/core";
41
42
  /** A url without its query or fragment. */
42
43
  const pathOnly = (url) => url.split(/[?#]/, 1)[0];
@@ -132,6 +133,7 @@ export class NativeTabsOutlet {
132
133
  const unsubscribeBar = back.addTabBar({
133
134
  unopenedTabOf: (url) => this.unopenedTabOf(url),
134
135
  behind: (url) => this.behind(url),
136
+ coveredAt: (url) => this.coveredAt(url),
135
137
  });
136
138
  inject(DestroyRef).onDestroy(() => {
137
139
  unsubscribeBack();
@@ -442,6 +444,13 @@ export class NativeTabsOutlet {
442
444
  const root = this.pathOf(entry);
443
445
  return pathOnly(url) === root ? null : root;
444
446
  }
447
+ /** Where the tab `url` is in was left, while a screen over the bar covers it. */
448
+ coveredAt(url) {
449
+ if (this.injector.get(SCREEN_IN_FRONT)())
450
+ return null;
451
+ const entry = this.tabOf(url);
452
+ return entry ? this.urlOf(entry) : null;
453
+ }
445
454
  /** Whether `url` is a tab other than the one in front, or a page inside one. */
446
455
  behind(url) {
447
456
  const entry = this.tabOf(url);
@@ -565,13 +574,14 @@ export class NativeTabsOutlet {
565
574
  });
566
575
  }
567
576
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: NativeTabsOutlet, deps: [], target: i0.ɵɵFactoryTarget.Component });
568
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.2.0", type: NativeTabsOutlet, isStandalone: true, selector: "native-tabs-outlet", inputs: { barHidden: { classPropertyName: "barHidden", publicName: "barHidden", isSignal: true, isRequired: false, transformFunction: null }, tintColor: { classPropertyName: "tintColor", publicName: "tintColor", isSignal: true, isRequired: false, transformFunction: null }, backgroundColor: { classPropertyName: "backgroundColor", publicName: "backgroundColor", isSignal: true, isRequired: false, transformFunction: null }, colorScheme: { classPropertyName: "colorScheme", publicName: "colorScheme", isSignal: true, isRequired: false, transformFunction: null }, minimizeBehavior: { classPropertyName: "minimizeBehavior", publicName: "minimizeBehavior", isSignal: true, isRequired: false, transformFunction: null }, controllerMode: { classPropertyName: "controllerMode", publicName: "controllerMode", isSignal: true, isRequired: false, transformFunction: null }, respectsKeyboard: { classPropertyName: "respectsKeyboard", publicName: "respectsKeyboard", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style": "fill", "tabBarHidden": "barHidden()", "tabBarTintColor": "tintColor() ?? defaults().tintColor", "nativeContainerBackgroundColor": "backgroundColor() ?? defaults().backgroundColor", "colorScheme": "colorScheme() ?? defaults().colorScheme", "tabBarMinimizeBehavior": "minimizeBehavior()", "tabBarControllerMode": "controllerMode()", "tabBarRespectsIMEInsets": "respectsKeyboard()" } }, queries: [{ propertyName: "declared", predicate: NativeTab, isSignal: true }], ngImport: i0, template: '<ng-content />', isInline: true });
577
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.2.0", type: NativeTabsOutlet, isStandalone: true, selector: "native-tabs-outlet", inputs: { barHidden: { classPropertyName: "barHidden", publicName: "barHidden", isSignal: true, isRequired: false, transformFunction: null }, tintColor: { classPropertyName: "tintColor", publicName: "tintColor", isSignal: true, isRequired: false, transformFunction: null }, backgroundColor: { classPropertyName: "backgroundColor", publicName: "backgroundColor", isSignal: true, isRequired: false, transformFunction: null }, colorScheme: { classPropertyName: "colorScheme", publicName: "colorScheme", isSignal: true, isRequired: false, transformFunction: null }, minimizeBehavior: { classPropertyName: "minimizeBehavior", publicName: "minimizeBehavior", isSignal: true, isRequired: false, transformFunction: null }, controllerMode: { classPropertyName: "controllerMode", publicName: "controllerMode", isSignal: true, isRequired: false, transformFunction: null }, respectsKeyboard: { classPropertyName: "respectsKeyboard", publicName: "respectsKeyboard", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style": "fill", "tabBarHidden": "barHidden()", "tabBarTintColor": "tintColor() ?? defaults().tintColor", "nativeContainerBackgroundColor": "backgroundColor() ?? defaults().backgroundColor", "colorScheme": "colorScheme() ?? defaults().colorScheme", "tabBarMinimizeBehavior": "minimizeBehavior()", "tabBarControllerMode": "controllerMode()", "tabBarRespectsIMEInsets": "respectsKeyboard()" } }, providers: [{ provide: SVG_ICON_HOST, useFactory: () => inject(ViewContainerRef) }], queries: [{ propertyName: "declared", predicate: NativeTab, isSignal: true }], ngImport: i0, template: '<ng-content />', isInline: true });
569
578
  }
570
579
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: NativeTabsOutlet, decorators: [{
571
580
  type: Component,
572
581
  args: [{
573
582
  selector: 'native-tabs-outlet',
574
583
  template: '<ng-content />',
584
+ providers: [{ provide: SVG_ICON_HOST, useFactory: () => inject(ViewContainerRef) }],
575
585
  host: {
576
586
  '[style]': 'fill',
577
587
  '[tabBarHidden]': 'barHidden()',