capacitor-native-tabbar 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,243 @@
1
+ package com.antonseagull.cap.tabbar
2
+
3
+ import android.content.res.ColorStateList
4
+ import android.content.Context
5
+ import android.graphics.Color
6
+ import android.graphics.Bitmap
7
+ import android.graphics.BitmapFactory
8
+ import android.graphics.drawable.BitmapDrawable
9
+ import android.graphics.drawable.Drawable
10
+ import android.util.Base64
11
+ import android.util.Log
12
+ import android.view.View
13
+ import android.view.Gravity
14
+ import android.view.ViewGroup
15
+ import androidx.core.content.ContextCompat
16
+ import androidx.core.view.ViewCompat
17
+ import androidx.core.view.WindowInsetsCompat
18
+ import androidx.coordinatorlayout.widget.CoordinatorLayout
19
+ import com.getcapacitor.JSArray
20
+ import com.google.android.material.bottomnavigation.BottomNavigationView
21
+ import com.google.android.material.navigation.NavigationBarView
22
+
23
+ class CapTabbar {
24
+
25
+ private var bottomNav: BottomNavigationView? = null
26
+ private var tabData: List<TabItemData> = emptyList()
27
+ private var selectedId: String = ""
28
+ private var parentView: ViewGroup? = null
29
+ private var onTabSelected: ((String) -> Unit)? = null
30
+
31
+ data class TabItemData(
32
+ val id: String,
33
+ val label: String,
34
+ val base64Icon: String?,
35
+ val base64ActiveIcon: String?
36
+ )
37
+
38
+ fun setTabSelectedCallback(callback: (String) -> Unit) {
39
+ onTabSelected = callback
40
+ }
41
+
42
+ private fun decodeBase64ToBitmap(base64: String?): Bitmap? {
43
+ if (base64.isNullOrEmpty()) return null
44
+ return try {
45
+ val cleanBase64 = base64
46
+ .replace("data:image/[^;]+;base64,".toRegex(), "")
47
+ .replace("\\s".toRegex(), "")
48
+ val decodedBytes = Base64.decode(cleanBase64, Base64.DEFAULT)
49
+ BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.size)
50
+ } catch (e: Exception) {
51
+ Log.e("CapTabbar", "Failed to decode base64 image", e)
52
+ null
53
+ }
54
+ }
55
+
56
+ /** Drawable name for bundle: lowercase, spaces/dashes -> underscore */
57
+ private fun drawableNameForId(id: String): String =
58
+ id.lowercase().replace(" ", "_").replace("-", "_").replace(Regex("[^a-z0-9_]"), "_")
59
+
60
+ private fun drawableFromBundle(context: Context, name: String): Drawable? {
61
+ val resId = context.resources.getIdentifier(name, "drawable", context.packageName)
62
+ return if (resId != 0) ContextCompat.getDrawable(context, resId) else null
63
+ }
64
+
65
+ private fun placeholderDrawable(context: Context): Drawable =
66
+ ContextCompat.getDrawable(context, R.drawable.cap_tabbar_placeholder)!!
67
+
68
+ private fun drawableForTab(context: Context, tab: TabItemData, isSelected: Boolean): Drawable {
69
+ val inactiveB64 = tab.base64Icon
70
+ val activeB64 = tab.base64ActiveIcon ?: tab.base64Icon
71
+ val base64 = if (isSelected) (activeB64 ?: inactiveB64) else (inactiveB64 ?: activeB64)
72
+ base64?.let { b64 ->
73
+ decodeBase64ToBitmap(b64)?.let { bitmap ->
74
+ val sizePx = (24 * context.resources.displayMetrics.density).toInt()
75
+ val scaled = if (bitmap.width != sizePx || bitmap.height != sizePx) {
76
+ Bitmap.createScaledBitmap(bitmap, sizePx, sizePx, true)
77
+ } else bitmap
78
+ val d = BitmapDrawable(context.resources, scaled)
79
+ d.setBounds(0, 0, sizePx, sizePx)
80
+ return d
81
+ }
82
+ }
83
+ val baseName = drawableNameForId(tab.id)
84
+ val inactiveDrawable = drawableFromBundle(context, baseName)
85
+ val activeDrawable = drawableFromBundle(context, "${baseName}_active")
86
+ val drawable = when {
87
+ isSelected -> activeDrawable ?: inactiveDrawable
88
+ else -> inactiveDrawable ?: activeDrawable
89
+ }
90
+ return drawable ?: placeholderDrawable(context)
91
+ }
92
+
93
+ private fun parseColorHex(hex: String?): Int? {
94
+ if (hex.isNullOrEmpty()) return null
95
+ val s = hex.trim().removePrefix("#")
96
+ return try {
97
+ when (s.length) {
98
+ 6 -> Color.parseColor("#$s")
99
+ 8 -> Color.parseColor("#$s")
100
+ else -> null
101
+ }
102
+ } catch (_: Exception) { null }
103
+ }
104
+
105
+ fun show(parent: ViewGroup, tabs: List<TabItemData>, selectedId: String, labelColor: String? = null, labelColorActive: String? = null) {
106
+ this.parentView = parent
107
+ this.tabData = tabs
108
+ this.selectedId = selectedId
109
+
110
+ removeTabBar()
111
+
112
+ val context = parent.context
113
+ val bottomNav = BottomNavigationView(context)
114
+ this.bottomNav = bottomNav
115
+
116
+ val density = context.resources.displayMetrics.density
117
+ val heightDp = 64 // достаточно для иконки + лейбла
118
+ val baseHeightPx = (heightDp * density).toInt()
119
+ val layoutParams = CoordinatorLayout.LayoutParams(
120
+ ViewGroup.LayoutParams.MATCH_PARENT,
121
+ baseHeightPx
122
+ ).apply {
123
+ gravity = Gravity.BOTTOM
124
+ }
125
+ bottomNav.layoutParams = layoutParams
126
+
127
+ fun applyBottomInset(view: View, bottomInset: Int) {
128
+ if (bottomInset > 0) {
129
+ view.setPadding(0, 0, 0, bottomInset)
130
+ (view.layoutParams as? ViewGroup.MarginLayoutParams)?.height = baseHeightPx + bottomInset
131
+ view.requestLayout()
132
+ }
133
+ }
134
+ ViewCompat.setOnApplyWindowInsetsListener(bottomNav) { view, insets ->
135
+ val bottomInset = insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom
136
+ applyBottomInset(view, bottomInset)
137
+ insets
138
+ }
139
+ bottomNav.post {
140
+ ViewCompat.getRootWindowInsets(bottomNav)?.let { insets ->
141
+ val bottomInset = insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom
142
+ applyBottomInset(bottomNav, bottomInset)
143
+ }
144
+ }
145
+ ViewCompat.requestApplyInsets(parent)
146
+ bottomNav.setBackgroundColor(0xFFFFFFFF.toInt())
147
+ bottomNav.itemIconTintList = null
148
+ bottomNav.labelVisibilityMode = NavigationBarView.LABEL_VISIBILITY_LABELED
149
+ bottomNav.itemIconSize = (24 * density).toInt()
150
+
151
+ val inactiveColor = parseColorHex(labelColor) ?: 0xFF000000.toInt()
152
+ val activeColor = parseColorHex(labelColorActive) ?: 0xFF007AFF.toInt()
153
+ bottomNav.itemTextColor = ColorStateList(
154
+ arrayOf(intArrayOf(android.R.attr.state_checked), intArrayOf(-android.R.attr.state_checked)),
155
+ intArrayOf(activeColor, inactiveColor)
156
+ )
157
+
158
+ val menu = bottomNav.menu
159
+ menu.clear()
160
+
161
+ tabs.forEachIndexed { index, tab ->
162
+ val item = menu.add(0, index, index, tab.label)
163
+ item.icon = drawableForTab(context, tab, tab.id == selectedId)
164
+ item.setCheckable(true)
165
+ }
166
+
167
+ bottomNav.selectedItemId = tabs.indexOfFirst { it.id == selectedId }.takeIf { it >= 0 } ?: 0
168
+
169
+ bottomNav.setOnItemSelectedListener { item ->
170
+ val tab = tabs.getOrNull(item.itemId) ?: return@setOnItemSelectedListener false
171
+ this.selectedId = tab.id
172
+ updateIcons()
173
+ onTabSelected?.invoke(tab.id)
174
+ true
175
+ }
176
+
177
+ bottomNav.setOnItemReselectedListener { item ->
178
+ val tab = tabs.getOrNull(item.itemId) ?: return@setOnItemReselectedListener
179
+ this.selectedId = tab.id
180
+ updateIcons()
181
+ onTabSelected?.invoke(tab.id)
182
+ }
183
+
184
+ parent.addView(bottomNav)
185
+ bottomNav.post { updateIcons() }
186
+ }
187
+
188
+ fun hide() {
189
+ removeTabBar()
190
+ }
191
+
192
+ fun getState(): Pair<Boolean, String> {
193
+ val visible = bottomNav != null
194
+ return Pair(visible, if (visible) selectedId else "")
195
+ }
196
+
197
+ fun setSelectedTab(tabId: String) {
198
+ val index = tabData.indexOfFirst { it.id == tabId }
199
+ if (index < 0) return
200
+ if (selectedId == tabId) return
201
+
202
+ selectedId = tabId
203
+ bottomNav?.selectedItemId = index
204
+ updateIcons()
205
+ }
206
+
207
+ private fun updateIcons() {
208
+ val nav = bottomNav ?: return
209
+ tabData.forEachIndexed { index, tab ->
210
+ val item = nav.menu.findItem(index) ?: return@forEachIndexed
211
+ item.icon = drawableForTab(nav.context, tab, tab.id == selectedId)
212
+ }
213
+ }
214
+
215
+ private fun removeTabBar() {
216
+ bottomNav?.let {
217
+ (it.parent as? ViewGroup)?.removeView(it)
218
+ }
219
+ bottomNav = null
220
+ }
221
+
222
+ companion object {
223
+ fun parseTabs(jsArray: JSArray): List<TabItemData> {
224
+ val list = mutableListOf<TabItemData>()
225
+ for (i in 0 until jsArray.length()) {
226
+ val obj = jsArray.getJSONObject(i) ?: continue
227
+ val id = obj.getString("id") ?: ""
228
+ if (id.isEmpty()) continue
229
+ val base64Icon = obj.optString("base64_icon").takeIf { it.isNotEmpty() }
230
+ val base64ActiveIcon = obj.optString("base64_active_icon").takeIf { it.isNotEmpty() } ?: base64Icon
231
+ list.add(
232
+ TabItemData(
233
+ id = id,
234
+ label = obj.getString("label") ?: "",
235
+ base64Icon = base64Icon,
236
+ base64ActiveIcon = base64ActiveIcon
237
+ )
238
+ )
239
+ }
240
+ return list
241
+ }
242
+ }
243
+ }
@@ -0,0 +1,87 @@
1
+ package com.antonseagull.cap.tabbar
2
+
3
+ import com.getcapacitor.JSArray
4
+ import com.getcapacitor.JSObject
5
+ import com.getcapacitor.Plugin
6
+ import com.getcapacitor.PluginCall
7
+ import com.getcapacitor.annotation.CapacitorPlugin
8
+ import com.getcapacitor.PluginMethod
9
+ import android.view.ViewGroup
10
+ import androidx.coordinatorlayout.widget.CoordinatorLayout
11
+
12
+ @CapacitorPlugin(name = "CapTabbar")
13
+ class CapTabbarPlugin : Plugin() {
14
+
15
+ private val implementation = CapTabbar()
16
+
17
+ override fun load() {
18
+ implementation.setTabSelectedCallback { tabId ->
19
+ val data = JSObject().apply {
20
+ put("tabId", tabId)
21
+ }
22
+ notifyListeners("tabChange", data)
23
+ }
24
+ }
25
+
26
+ @PluginMethod
27
+ fun show(call: PluginCall) {
28
+ val tabsArray = call.getArray("tabs")
29
+ val selectedId = call.getString("selectedId")
30
+
31
+ if (tabsArray == null || selectedId == null) {
32
+ call.reject("tabs and selectedId are required")
33
+ return
34
+ }
35
+
36
+ val tabs = CapTabbar.parseTabs(tabsArray)
37
+ if (tabs.isEmpty()) {
38
+ call.reject("At least one tab is required")
39
+ return
40
+ }
41
+
42
+ val labelColor = call.getString("label_color")
43
+ val labelColorActive = call.getString("label_color_active")
44
+
45
+ getActivity().runOnUiThread {
46
+ val webView = bridge.webView
47
+ val parent = webView.parent as? ViewGroup
48
+ if (parent == null || parent !is CoordinatorLayout) {
49
+ call.reject("Could not find parent view to attach tab bar")
50
+ return@runOnUiThread
51
+ }
52
+ implementation.show(parent, tabs, selectedId, labelColor, labelColorActive)
53
+ call.resolve()
54
+ }
55
+ }
56
+
57
+ @PluginMethod
58
+ fun hide(call: PluginCall) {
59
+ getActivity().runOnUiThread {
60
+ implementation.hide()
61
+ call.resolve()
62
+ }
63
+ }
64
+
65
+ @PluginMethod
66
+ fun setSelectedTab(call: PluginCall) {
67
+ val tabId = call.getString("tabId")
68
+ if (tabId == null) {
69
+ call.reject("tabId is required")
70
+ return
71
+ }
72
+ getActivity().runOnUiThread {
73
+ implementation.setSelectedTab(tabId)
74
+ call.resolve()
75
+ }
76
+ }
77
+
78
+ @PluginMethod
79
+ fun getState(call: PluginCall) {
80
+ val state = implementation.getState()
81
+ val result = JSObject().apply {
82
+ put("visible", state.first)
83
+ put("activeTabId", state.second)
84
+ }
85
+ call.resolve(result)
86
+ }
87
+ }
File without changes
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Placeholder when tab icon is missing - broken_image style -->
3
+ <vector xmlns:android="http://schemas.android.com/apk/res/android"
4
+ android:width="24dp"
5
+ android:height="24dp"
6
+ android:viewportWidth="24"
7
+ android:viewportHeight="24">
8
+ <path
9
+ android:fillColor="#999999"
10
+ android:pathData="M21,5v6.59l-3,-3.01 -4,4.01 -4,-4 -4,4 -3,-3.01V5c0,-1.1 0.9,-2 2,-2h14c1.1,0 2,0.9 2,2zM18,11.42l3,3.01V19c0,1.1 -0.9,2 -2,2H5c-1.1,0 -2,-0.9 -2,-2v-6.58l3,2.99 4,-4 4,4 4,-3.99z"/>
11
+ </vector>
package/dist/docs.json ADDED
@@ -0,0 +1,228 @@
1
+ {
2
+ "api": {
3
+ "name": "CapTabbarPlugin",
4
+ "slug": "captabbarplugin",
5
+ "docs": "",
6
+ "tags": [],
7
+ "methods": [
8
+ {
9
+ "name": "show",
10
+ "signature": "(options: ShowOptions) => Promise<void>",
11
+ "parameters": [
12
+ {
13
+ "name": "options",
14
+ "docs": "",
15
+ "type": "ShowOptions"
16
+ }
17
+ ],
18
+ "returns": "Promise<void>",
19
+ "tags": [],
20
+ "docs": "Show the native tab bar with the given configuration",
21
+ "complexTypes": [
22
+ "ShowOptions"
23
+ ],
24
+ "slug": "show"
25
+ },
26
+ {
27
+ "name": "hide",
28
+ "signature": "() => Promise<void>",
29
+ "parameters": [],
30
+ "returns": "Promise<void>",
31
+ "tags": [],
32
+ "docs": "Hide the tab bar",
33
+ "complexTypes": [],
34
+ "slug": "hide"
35
+ },
36
+ {
37
+ "name": "setSelectedTab",
38
+ "signature": "(options: SetSelectedTabOptions) => Promise<void>",
39
+ "parameters": [
40
+ {
41
+ "name": "options",
42
+ "docs": "",
43
+ "type": "SetSelectedTabOptions"
44
+ }
45
+ ],
46
+ "returns": "Promise<void>",
47
+ "tags": [],
48
+ "docs": "Programmatically set the selected tab",
49
+ "complexTypes": [
50
+ "SetSelectedTabOptions"
51
+ ],
52
+ "slug": "setselectedtab"
53
+ },
54
+ {
55
+ "name": "getState",
56
+ "signature": "() => Promise<GetStateResult>",
57
+ "parameters": [],
58
+ "returns": "Promise<GetStateResult>",
59
+ "tags": [],
60
+ "docs": "Get current state: visible and active tab id",
61
+ "complexTypes": [
62
+ "GetStateResult"
63
+ ],
64
+ "slug": "getstate"
65
+ },
66
+ {
67
+ "name": "addListener",
68
+ "signature": "(eventName: 'tabChange', listenerFunc: (event: TabChangeEvent) => void) => Promise<{ remove: () => Promise<void>; }>",
69
+ "parameters": [
70
+ {
71
+ "name": "eventName",
72
+ "docs": "",
73
+ "type": "'tabChange'"
74
+ },
75
+ {
76
+ "name": "listenerFunc",
77
+ "docs": "",
78
+ "type": "(event: TabChangeEvent) => void"
79
+ }
80
+ ],
81
+ "returns": "Promise<{ remove: () => Promise<void>; }>",
82
+ "tags": [],
83
+ "docs": "Add listener for tab change events (when user taps a tab)",
84
+ "complexTypes": [
85
+ "TabChangeEvent"
86
+ ],
87
+ "slug": "addlistenertabchange-"
88
+ }
89
+ ],
90
+ "properties": []
91
+ },
92
+ "interfaces": [
93
+ {
94
+ "name": "ShowOptions",
95
+ "slug": "showoptions",
96
+ "docs": "Icons: 2 variants (base64 has priority over bundle)\n\n1) **Base64** – if `base64_icon` / `base64_active_icon` are set, they are used.\n\n2) **Bundle assets** – if base64 not set, icons are loaded from native resources:\n\n**Android** – `android/app/src/main/res/drawable/`:\n - `{id}.png` or `{id}.xml` – inactive icon\n - `{id}_active.png` or `{id}_active.xml` – active icon\n\n**iOS** – Assets.xcassets: Image Set `{id}` and `{id}_active`.\n\nIf an icon is missing, a placeholder is shown.",
97
+ "tags": [],
98
+ "methods": [],
99
+ "properties": [
100
+ {
101
+ "name": "tabs",
102
+ "tags": [],
103
+ "docs": "Array of tab items to display",
104
+ "complexTypes": [
105
+ "TabItem"
106
+ ],
107
+ "type": "TabItem[]"
108
+ },
109
+ {
110
+ "name": "selectedId",
111
+ "tags": [],
112
+ "docs": "ID of the initially selected tab",
113
+ "complexTypes": [],
114
+ "type": "string"
115
+ },
116
+ {
117
+ "name": "label_color",
118
+ "tags": [],
119
+ "docs": "Label color for inactive tabs (hex, e.g. \"#000000\"). Default: black",
120
+ "complexTypes": [],
121
+ "type": "string | undefined"
122
+ },
123
+ {
124
+ "name": "label_color_active",
125
+ "tags": [],
126
+ "docs": "Label color for selected tab (hex, e.g. \"#007AFF\"). Default: blue",
127
+ "complexTypes": [],
128
+ "type": "string | undefined"
129
+ }
130
+ ]
131
+ },
132
+ {
133
+ "name": "TabItem",
134
+ "slug": "tabitem",
135
+ "docs": "",
136
+ "tags": [],
137
+ "methods": [],
138
+ "properties": [
139
+ {
140
+ "name": "id",
141
+ "tags": [],
142
+ "docs": "Unique identifier for the tab. Used for bundle assets: {id}.png, {id}_active.png",
143
+ "complexTypes": [],
144
+ "type": "string"
145
+ },
146
+ {
147
+ "name": "label",
148
+ "tags": [],
149
+ "docs": "Display label",
150
+ "complexTypes": [],
151
+ "type": "string"
152
+ },
153
+ {
154
+ "name": "base64_icon",
155
+ "tags": [],
156
+ "docs": "Base64-encoded image for inactive state. If set, overrides bundle assets",
157
+ "complexTypes": [],
158
+ "type": "string | undefined"
159
+ },
160
+ {
161
+ "name": "base64_active_icon",
162
+ "tags": [],
163
+ "docs": "Base64-encoded image for active/selected state. If set, overrides bundle assets",
164
+ "complexTypes": [],
165
+ "type": "string | undefined"
166
+ }
167
+ ]
168
+ },
169
+ {
170
+ "name": "SetSelectedTabOptions",
171
+ "slug": "setselectedtaboptions",
172
+ "docs": "",
173
+ "tags": [],
174
+ "methods": [],
175
+ "properties": [
176
+ {
177
+ "name": "tabId",
178
+ "tags": [],
179
+ "docs": "ID of the tab to select",
180
+ "complexTypes": [],
181
+ "type": "string"
182
+ }
183
+ ]
184
+ },
185
+ {
186
+ "name": "GetStateResult",
187
+ "slug": "getstateresult",
188
+ "docs": "",
189
+ "tags": [],
190
+ "methods": [],
191
+ "properties": [
192
+ {
193
+ "name": "visible",
194
+ "tags": [],
195
+ "docs": "Whether the tab bar is currently shown",
196
+ "complexTypes": [],
197
+ "type": "boolean"
198
+ },
199
+ {
200
+ "name": "activeTabId",
201
+ "tags": [],
202
+ "docs": "ID of the active/selected tab (empty when hidden)",
203
+ "complexTypes": [],
204
+ "type": "string"
205
+ }
206
+ ]
207
+ },
208
+ {
209
+ "name": "TabChangeEvent",
210
+ "slug": "tabchangeevent",
211
+ "docs": "",
212
+ "tags": [],
213
+ "methods": [],
214
+ "properties": [
215
+ {
216
+ "name": "tabId",
217
+ "tags": [],
218
+ "docs": "ID of the newly selected tab",
219
+ "complexTypes": [],
220
+ "type": "string"
221
+ }
222
+ ]
223
+ }
224
+ ],
225
+ "enums": [],
226
+ "typeAliases": [],
227
+ "pluginConfigs": []
228
+ }
@@ -0,0 +1,63 @@
1
+ export interface TabItem {
2
+ /** Unique identifier for the tab. Used for bundle assets: {id}.png, {id}_active.png */
3
+ id: string;
4
+ /** Display label */
5
+ label: string;
6
+ /** Base64-encoded image for inactive state. If set, overrides bundle assets */
7
+ base64_icon?: string;
8
+ /** Base64-encoded image for active/selected state. If set, overrides bundle assets */
9
+ base64_active_icon?: string;
10
+ }
11
+ /**
12
+ * Icons: 2 variants (base64 has priority over bundle)
13
+ *
14
+ * 1) **Base64** – if `base64_icon` / `base64_active_icon` are set, they are used.
15
+ *
16
+ * 2) **Bundle assets** – if base64 not set, icons are loaded from native resources:
17
+ *
18
+ * **Android** – `android/app/src/main/res/drawable/`:
19
+ * - `{id}.png` or `{id}.xml` – inactive icon
20
+ * - `{id}_active.png` or `{id}_active.xml` – active icon
21
+ *
22
+ * **iOS** – Assets.xcassets: Image Set `{id}` and `{id}_active`.
23
+ *
24
+ * If an icon is missing, a placeholder is shown.
25
+ */
26
+ export interface ShowOptions {
27
+ /** Array of tab items to display */
28
+ tabs: TabItem[];
29
+ /** ID of the initially selected tab */
30
+ selectedId: string;
31
+ /** Label color for inactive tabs (hex, e.g. "#000000"). Default: black */
32
+ label_color?: string;
33
+ /** Label color for selected tab (hex, e.g. "#007AFF"). Default: blue */
34
+ label_color_active?: string;
35
+ }
36
+ export interface SetSelectedTabOptions {
37
+ /** ID of the tab to select */
38
+ tabId: string;
39
+ }
40
+ export interface TabChangeEvent {
41
+ /** ID of the newly selected tab */
42
+ tabId: string;
43
+ }
44
+ export interface GetStateResult {
45
+ /** Whether the tab bar is currently shown */
46
+ visible: boolean;
47
+ /** ID of the active/selected tab (empty when hidden) */
48
+ activeTabId: string;
49
+ }
50
+ export interface CapTabbarPlugin {
51
+ /** Show the native tab bar with the given configuration */
52
+ show(options: ShowOptions): Promise<void>;
53
+ /** Hide the tab bar */
54
+ hide(): Promise<void>;
55
+ /** Programmatically set the selected tab */
56
+ setSelectedTab(options: SetSelectedTabOptions): Promise<void>;
57
+ /** Get current state: visible and active tab id */
58
+ getState(): Promise<GetStateResult>;
59
+ /** Add listener for tab change events (when user taps a tab) */
60
+ addListener(eventName: 'tabChange', listenerFunc: (event: TabChangeEvent) => void): Promise<{
61
+ remove: () => Promise<void>;
62
+ }>;
63
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=definitions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["export interface TabItem {\n /** Unique identifier for the tab. Used for bundle assets: {id}.png, {id}_active.png */\n id: string;\n /** Display label */\n label: string;\n /** Base64-encoded image for inactive state. If set, overrides bundle assets */\n base64_icon?: string;\n /** Base64-encoded image for active/selected state. If set, overrides bundle assets */\n base64_active_icon?: string;\n}\n\n/**\n * Icons: 2 variants (base64 has priority over bundle)\n *\n * 1) **Base64** – if `base64_icon` / `base64_active_icon` are set, they are used.\n *\n * 2) **Bundle assets** – if base64 not set, icons are loaded from native resources:\n *\n * **Android** – `android/app/src/main/res/drawable/`:\n * - `{id}.png` or `{id}.xml` – inactive icon\n * - `{id}_active.png` or `{id}_active.xml` – active icon\n *\n * **iOS** – Assets.xcassets: Image Set `{id}` and `{id}_active`.\n *\n * If an icon is missing, a placeholder is shown.\n */\n\nexport interface ShowOptions {\n /** Array of tab items to display */\n tabs: TabItem[];\n /** ID of the initially selected tab */\n selectedId: string;\n /** Label color for inactive tabs (hex, e.g. \"#000000\"). Default: black */\n label_color?: string;\n /** Label color for selected tab (hex, e.g. \"#007AFF\"). Default: blue */\n label_color_active?: string;\n}\n\nexport interface SetSelectedTabOptions {\n /** ID of the tab to select */\n tabId: string;\n}\n\nexport interface TabChangeEvent {\n /** ID of the newly selected tab */\n tabId: string;\n}\n\nexport interface GetStateResult {\n /** Whether the tab bar is currently shown */\n visible: boolean;\n /** ID of the active/selected tab (empty when hidden) */\n activeTabId: string;\n}\n\nexport interface CapTabbarPlugin {\n /** Show the native tab bar with the given configuration */\n show(options: ShowOptions): Promise<void>;\n\n /** Hide the tab bar */\n hide(): Promise<void>;\n\n /** Programmatically set the selected tab */\n setSelectedTab(options: SetSelectedTabOptions): Promise<void>;\n\n /** Get current state: visible and active tab id */\n getState(): Promise<GetStateResult>;\n\n /** Add listener for tab change events (when user taps a tab) */\n addListener(\n eventName: 'tabChange',\n listenerFunc: (event: TabChangeEvent) => void\n ): Promise<{ remove: () => Promise<void> }>;\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import type { CapTabbarPlugin } from './definitions';
2
+ declare const CapTabbar: CapTabbarPlugin;
3
+ export * from './definitions';
4
+ export { CapTabbar };
@@ -0,0 +1,7 @@
1
+ import { registerPlugin } from '@capacitor/core';
2
+ const CapTabbar = registerPlugin('CapTabbar', {
3
+ web: () => import('./web').then((m) => new m.CapTabbarWeb()),
4
+ });
5
+ export * from './definitions';
6
+ export { CapTabbar };
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,SAAS,GAAG,cAAc,CAAkB,WAAW,EAAE;IAC7D,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,CAAC;CAC7D,CAAC,CAAC;AAEH,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,SAAS,EAAE,CAAC","sourcesContent":["import { registerPlugin } from '@capacitor/core';\n\nimport type { CapTabbarPlugin } from './definitions';\n\nconst CapTabbar = registerPlugin<CapTabbarPlugin>('CapTabbar', {\n web: () => import('./web').then((m) => new m.CapTabbarWeb()),\n});\n\nexport * from './definitions';\nexport { CapTabbar };\n"]}
@@ -0,0 +1,19 @@
1
+ import { WebPlugin } from '@capacitor/core';
2
+ import type { CapTabbarPlugin, GetStateResult, ShowOptions, SetSelectedTabOptions } from './definitions';
3
+ export declare class CapTabbarWeb extends WebPlugin implements CapTabbarPlugin {
4
+ private tabBarContainer;
5
+ private tabItems;
6
+ private selectedId;
7
+ private labelColor;
8
+ private labelColorActive;
9
+ private tabElements;
10
+ show(options: ShowOptions): Promise<void>;
11
+ hide(): Promise<void>;
12
+ setSelectedTab(options: SetSelectedTabOptions): Promise<void>;
13
+ getState(): Promise<GetStateResult>;
14
+ private base64ToDataUrl;
15
+ private removeTabBar;
16
+ private createTabBar;
17
+ private handleTabClick;
18
+ private updateTabBarSelection;
19
+ }