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.
- package/CapacitorNativeTabbar.podspec +17 -0
- package/Package.swift +28 -0
- package/README.md +262 -0
- package/README.ru.md +168 -0
- package/android/build.gradle +68 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/kotlin/com/antonseagull/cap/tabbar/CapTabbar.kt +243 -0
- package/android/src/main/kotlin/com/antonseagull/cap/tabbar/CapTabbarPlugin.kt +87 -0
- package/android/src/main/res/.gitkeep +0 -0
- package/android/src/main/res/drawable/cap_tabbar_placeholder.xml +11 -0
- package/dist/docs.json +228 -0
- package/dist/esm/definitions.d.ts +63 -0
- package/dist/esm/definitions.js +2 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +19 -0
- package/dist/esm/web.js +128 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +142 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +145 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Sources/CapTabbarPlugin/CapTabbar.swift +246 -0
- package/ios/Sources/CapTabbarPlugin/CapTabbarPlugin.swift +75 -0
- package/ios/Tests/CapTabbarPluginTests/CapTabbarTests.swift +13 -0
- package/package.json +86 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import UIKit
|
|
3
|
+
import Capacitor
|
|
4
|
+
import WebKit
|
|
5
|
+
|
|
6
|
+
@objc public class CapTabbar: NSObject {
|
|
7
|
+
private var tabBar: UITabBar?
|
|
8
|
+
private var tabItems: [TabItemData] = []
|
|
9
|
+
private var selectedId: String = ""
|
|
10
|
+
private weak var parentView: UIView?
|
|
11
|
+
private var onTabSelected: ((String) -> Void)?
|
|
12
|
+
private var heightConstraint: NSLayoutConstraint?
|
|
13
|
+
private var baseContentInset: UIEdgeInsets = .zero
|
|
14
|
+
private var baseIndicatorInset: UIEdgeInsets = .zero
|
|
15
|
+
private var insetsCaptured = false
|
|
16
|
+
|
|
17
|
+
struct TabItemData {
|
|
18
|
+
let id: String
|
|
19
|
+
let label: String
|
|
20
|
+
let base64Icon: String?
|
|
21
|
+
let base64ActiveIcon: String?
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
func setTabSelectedCallback(_ callback: @escaping (String) -> Void) {
|
|
25
|
+
onTabSelected = callback
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
func decodeBase64ToImage(_ base64: String?) -> UIImage? {
|
|
29
|
+
guard let base64 = base64, !base64.isEmpty else { return nil }
|
|
30
|
+
let cleaned = base64.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
31
|
+
let data: Data?
|
|
32
|
+
if cleaned.hasPrefix("data:") {
|
|
33
|
+
data = URL(string: cleaned).flatMap { try? Data(contentsOf: $0) }
|
|
34
|
+
} else {
|
|
35
|
+
let base64Part = cleaned.contains(",") ? String(cleaned.split(separator: ",").last ?? "") : cleaned
|
|
36
|
+
data = Data(base64Encoded: base64Part, options: .ignoreUnknownCharacters)
|
|
37
|
+
}
|
|
38
|
+
guard let imageData = data, let image = UIImage(data: imageData) else { return nil }
|
|
39
|
+
if image.size.width < 2 || image.size.height < 2 { return nil }
|
|
40
|
+
return image.withRenderingMode(.alwaysOriginal)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private func placeholderImage() -> UIImage {
|
|
44
|
+
return (UIImage(systemName: "photo.badge.exclamationmark") ?? UIImage(systemName: "circle.fill") ?? UIImage())
|
|
45
|
+
.withRenderingMode(.alwaysTemplate)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private func tabBarHeight() -> CGFloat {
|
|
49
|
+
guard let parent = parentView else { return 49 }
|
|
50
|
+
return 49 + parent.safeAreaInsets.bottom
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private func uiColorFromHex(_ hex: String?) -> UIColor? {
|
|
54
|
+
guard var hex = hex?.trimmingCharacters(in: .whitespacesAndNewlines), !hex.isEmpty else { return nil }
|
|
55
|
+
if hex.hasPrefix("#") { hex = String(hex.dropFirst()) }
|
|
56
|
+
var rgb: UInt64 = 0
|
|
57
|
+
guard Scanner(string: hex).scanHexInt64(&rgb) else { return nil }
|
|
58
|
+
let r, g, b, a: CGFloat
|
|
59
|
+
if hex.count == 6 {
|
|
60
|
+
r = CGFloat((rgb >> 16) & 0xff) / 255
|
|
61
|
+
g = CGFloat((rgb >> 8) & 0xff) / 255
|
|
62
|
+
b = CGFloat(rgb & 0xff) / 255
|
|
63
|
+
a = 1
|
|
64
|
+
} else if hex.count == 8 {
|
|
65
|
+
r = CGFloat((rgb >> 24) & 0xff) / 255
|
|
66
|
+
g = CGFloat((rgb >> 16) & 0xff) / 255
|
|
67
|
+
b = CGFloat((rgb >> 8) & 0xff) / 255
|
|
68
|
+
a = CGFloat(rgb & 0xff) / 255
|
|
69
|
+
} else { return nil }
|
|
70
|
+
return UIColor(red: r, green: g, blue: b, alpha: a)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
func show(parent: UIView, tabs: [TabItemData], selectedId: String, labelColor: String? = nil, labelColorActive: String? = nil) {
|
|
74
|
+
self.parentView = parent
|
|
75
|
+
self.tabItems = tabs
|
|
76
|
+
self.selectedId = selectedId
|
|
77
|
+
|
|
78
|
+
removeTabBar()
|
|
79
|
+
|
|
80
|
+
let tabBar = UITabBar()
|
|
81
|
+
tabBar.delegate = self
|
|
82
|
+
tabBar.translatesAutoresizingMaskIntoConstraints = false
|
|
83
|
+
|
|
84
|
+
let appearance = UITabBarAppearance()
|
|
85
|
+
appearance.configureWithDefaultBackground()
|
|
86
|
+
let normalColor = uiColorFromHex(labelColor) ?? UIColor.label
|
|
87
|
+
let selectedColor = uiColorFromHex(labelColorActive) ?? UIColor.systemBlue
|
|
88
|
+
appearance.stackedLayoutAppearance.normal.titleTextAttributes = [.foregroundColor: normalColor]
|
|
89
|
+
appearance.stackedLayoutAppearance.selected.titleTextAttributes = [.foregroundColor: selectedColor]
|
|
90
|
+
tabBar.standardAppearance = appearance
|
|
91
|
+
if #available(iOS 15.0, *) {
|
|
92
|
+
tabBar.scrollEdgeAppearance = appearance
|
|
93
|
+
}
|
|
94
|
+
self.tabBar = tabBar
|
|
95
|
+
|
|
96
|
+
var items: [UITabBarItem] = []
|
|
97
|
+
for (index, tab) in tabs.enumerated() {
|
|
98
|
+
let (image, selectedImage) = imageForTab(tab, isSelected: tab.id == selectedId)
|
|
99
|
+
let item = UITabBarItem(title: tab.label, image: image, selectedImage: selectedImage)
|
|
100
|
+
item.tag = index
|
|
101
|
+
items.append(item)
|
|
102
|
+
}
|
|
103
|
+
tabBar.setItems(items, animated: false)
|
|
104
|
+
|
|
105
|
+
let selectedIndex = tabs.firstIndex(where: { $0.id == selectedId }) ?? 0
|
|
106
|
+
tabBar.selectedItem = items[selectedIndex]
|
|
107
|
+
|
|
108
|
+
parent.addSubview(tabBar)
|
|
109
|
+
let height = tabBarHeight()
|
|
110
|
+
heightConstraint = tabBar.heightAnchor.constraint(equalToConstant: height)
|
|
111
|
+
NSLayoutConstraint.activate([
|
|
112
|
+
tabBar.leadingAnchor.constraint(equalTo: parent.leadingAnchor),
|
|
113
|
+
tabBar.trailingAnchor.constraint(equalTo: parent.trailingAnchor),
|
|
114
|
+
tabBar.bottomAnchor.constraint(equalTo: parent.bottomAnchor),
|
|
115
|
+
heightConstraint!
|
|
116
|
+
])
|
|
117
|
+
|
|
118
|
+
captureBaseInsetsIfNeeded(from: parent)
|
|
119
|
+
applyWebViewInsets(to: parent)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
func hide() {
|
|
123
|
+
guard let parent = parentView else { return removeTabBar() }
|
|
124
|
+
removeWebViewInsets(from: parent)
|
|
125
|
+
removeTabBar()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
func setSelectedTab(tabId: String) {
|
|
129
|
+
guard let index = tabItems.firstIndex(where: { $0.id == tabId }) else { return }
|
|
130
|
+
guard selectedId != tabId else { return }
|
|
131
|
+
selectedId = tabId
|
|
132
|
+
tabBar?.selectedItem = tabBar?.items?[index]
|
|
133
|
+
updateItemImages()
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
func getState() -> (visible: Bool, activeTabId: String) {
|
|
137
|
+
let visible = tabBar != nil
|
|
138
|
+
return (visible, visible ? selectedId : "")
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private func imageFromBundle(_ name: String) -> UIImage? {
|
|
142
|
+
UIImage(named: name)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private func imageForTab(_ tab: TabItemData, isSelected: Bool) -> (UIImage, UIImage) {
|
|
146
|
+
let inactiveB64 = tab.base64Icon
|
|
147
|
+
let activeB64 = tab.base64ActiveIcon ?? tab.base64Icon
|
|
148
|
+
if let iconStr = inactiveB64, let img = decodeBase64ToImage(iconStr) {
|
|
149
|
+
let activeImg = (activeB64 != nil ? decodeBase64ToImage(activeB64 ?? "") : nil) ?? img
|
|
150
|
+
return (resizeForTabBar(img) ?? placeholderImage(), resizeForTabBar(activeImg) ?? placeholderImage())
|
|
151
|
+
}
|
|
152
|
+
let bundleInactive = imageFromBundle(tab.id) ?? imageFromBundle(tab.id.replacingOccurrences(of: " ", with: "_").replacingOccurrences(of: "-", with: "_"))
|
|
153
|
+
let bundleActive = imageFromBundle("\(tab.id)_active") ?? imageFromBundle("\(tab.id)_active".replacingOccurrences(of: " ", with: "_").replacingOccurrences(of: "-", with: "_"))
|
|
154
|
+
return (
|
|
155
|
+
resizeForTabBar(bundleInactive) ?? placeholderImage(),
|
|
156
|
+
resizeForTabBar(bundleActive ?? bundleInactive) ?? placeholderImage()
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
private func resizeForTabBar(_ image: UIImage?) -> UIImage? {
|
|
161
|
+
guard let image = image else { return nil }
|
|
162
|
+
let maxSize: CGFloat = 25
|
|
163
|
+
let size = image.size
|
|
164
|
+
guard size.width > maxSize || size.height > maxSize else { return image.withRenderingMode(.alwaysOriginal) }
|
|
165
|
+
let scale = min(maxSize / size.width, maxSize / size.height)
|
|
166
|
+
let newSize = CGSize(width: size.width * scale, height: size.height * scale)
|
|
167
|
+
let renderer = UIGraphicsImageRenderer(size: newSize)
|
|
168
|
+
let resized = renderer.image { _ in image.draw(in: CGRect(origin: .zero, size: newSize)) }
|
|
169
|
+
return resized.withRenderingMode(.alwaysOriginal)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private func updateItemImages() {
|
|
173
|
+
guard let tabBar = tabBar, let items = tabBar.items else { return }
|
|
174
|
+
for (index, tab) in tabItems.enumerated() where index < items.count {
|
|
175
|
+
let (image, selectedImage) = imageForTab(tab, isSelected: tab.id == selectedId)
|
|
176
|
+
items[index].image = image
|
|
177
|
+
items[index].selectedImage = selectedImage
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private func removeTabBar() {
|
|
182
|
+
tabBar?.removeFromSuperview()
|
|
183
|
+
tabBar = nil
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private func webViewScrollView(from view: UIView) -> UIScrollView? {
|
|
187
|
+
(view as? WKWebView)?.scrollView
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private func captureBaseInsetsIfNeeded(from parent: UIView) {
|
|
191
|
+
guard !insetsCaptured, let scrollView = webViewScrollView(from: parent) else { return }
|
|
192
|
+
baseContentInset = scrollView.contentInset
|
|
193
|
+
baseIndicatorInset = scrollView.verticalScrollIndicatorInsets
|
|
194
|
+
insetsCaptured = true
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private func applyWebViewInsets(to parent: UIView) {
|
|
198
|
+
guard let scrollView = webViewScrollView(from: parent) else { return }
|
|
199
|
+
captureBaseInsetsIfNeeded(from: parent)
|
|
200
|
+
let extra = tabBarHeight()
|
|
201
|
+
var content = baseContentInset
|
|
202
|
+
content.bottom = max(content.bottom, baseContentInset.bottom + extra)
|
|
203
|
+
var indicators = baseIndicatorInset
|
|
204
|
+
indicators.bottom = max(indicators.bottom, baseIndicatorInset.bottom + extra)
|
|
205
|
+
scrollView.contentInset = content
|
|
206
|
+
scrollView.verticalScrollIndicatorInsets = indicators
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
private func removeWebViewInsets(from parent: UIView) {
|
|
210
|
+
guard let scrollView = webViewScrollView(from: parent), insetsCaptured else { return }
|
|
211
|
+
scrollView.contentInset = baseContentInset
|
|
212
|
+
scrollView.verticalScrollIndicatorInsets = baseIndicatorInset
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
static func parseTabs(from array: [JSValue]) -> [TabItemData] {
|
|
216
|
+
return array.compactMap { value -> TabItemData? in
|
|
217
|
+
guard let dict = value as? JSObject,
|
|
218
|
+
let id = parseString(dict["id"]),
|
|
219
|
+
let label = parseString(dict["label"]) else { return nil }
|
|
220
|
+
let base64Icon = parseString(dict["base64_icon"])
|
|
221
|
+
let base64ActiveIcon = parseString(dict["base64_active_icon"]) ?? base64Icon
|
|
222
|
+
guard !id.isEmpty else { return nil }
|
|
223
|
+
return TabItemData(id: id, label: label, base64Icon: base64Icon, base64ActiveIcon: base64ActiveIcon)
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
private static func parseString(_ value: JSValue?) -> String? {
|
|
228
|
+
guard let value = value else { return nil }
|
|
229
|
+
if let s = value as? String { return s }
|
|
230
|
+
if let ns = value as? NSString { return String(ns) }
|
|
231
|
+
return nil
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
extension CapTabbar: UITabBarDelegate {
|
|
236
|
+
public func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
|
|
237
|
+
let index = item.tag
|
|
238
|
+
guard index < tabItems.count else { return }
|
|
239
|
+
let tab = tabItems[index]
|
|
240
|
+
if selectedId != tab.id {
|
|
241
|
+
selectedId = tab.id
|
|
242
|
+
updateItemImages()
|
|
243
|
+
onTabSelected?(tab.id)
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import Capacitor
|
|
3
|
+
import UIKit
|
|
4
|
+
|
|
5
|
+
@objc(CapTabbarPlugin)
|
|
6
|
+
public class CapTabbarPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
7
|
+
public let identifier = "CapTabbarPlugin"
|
|
8
|
+
public let jsName = "CapTabbar"
|
|
9
|
+
public let pluginMethods: [CAPPluginMethod] = [
|
|
10
|
+
CAPPluginMethod(name: "show", returnType: CAPPluginReturnPromise),
|
|
11
|
+
CAPPluginMethod(name: "hide", returnType: CAPPluginReturnPromise),
|
|
12
|
+
CAPPluginMethod(name: "setSelectedTab", returnType: CAPPluginReturnPromise),
|
|
13
|
+
CAPPluginMethod(name: "getState", returnType: CAPPluginReturnPromise)
|
|
14
|
+
]
|
|
15
|
+
private let implementation = CapTabbar()
|
|
16
|
+
|
|
17
|
+
public override func load() {
|
|
18
|
+
implementation.setTabSelectedCallback { [weak self] tabId in
|
|
19
|
+
self?.notifyListeners("tabChange", data: ["tabId": tabId])
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@objc func show(_ call: CAPPluginCall) {
|
|
24
|
+
guard let tabsArray = call.getArray("tabs"),
|
|
25
|
+
let selectedId = call.getString("selectedId") else {
|
|
26
|
+
call.reject("tabs and selectedId are required")
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let tabs = CapTabbar.parseTabs(from: tabsArray)
|
|
31
|
+
guard !tabs.isEmpty else {
|
|
32
|
+
call.reject("At least one tab is required")
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let labelColor = call.getString("label_color")
|
|
37
|
+
let labelColorActive = call.getString("label_color_active")
|
|
38
|
+
DispatchQueue.main.async { [weak self] in
|
|
39
|
+
guard let self = self,
|
|
40
|
+
let viewController = self.bridge?.viewController,
|
|
41
|
+
let view = viewController.view else {
|
|
42
|
+
call.reject("Could not find view to attach tab bar")
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
self.implementation.show(parent: view, tabs: tabs, selectedId: selectedId, labelColor: labelColor, labelColorActive: labelColorActive)
|
|
46
|
+
call.resolve()
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@objc func hide(_ call: CAPPluginCall) {
|
|
51
|
+
DispatchQueue.main.async { [weak self] in
|
|
52
|
+
self?.implementation.hide()
|
|
53
|
+
call.resolve()
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@objc func setSelectedTab(_ call: CAPPluginCall) {
|
|
58
|
+
guard let tabId = call.getString("tabId") else {
|
|
59
|
+
call.reject("tabId is required")
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
DispatchQueue.main.async { [weak self] in
|
|
63
|
+
self?.implementation.setSelectedTab(tabId: tabId)
|
|
64
|
+
call.resolve()
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@objc func getState(_ call: CAPPluginCall) {
|
|
69
|
+
let state = implementation.getState()
|
|
70
|
+
call.resolve([
|
|
71
|
+
"visible": state.visible,
|
|
72
|
+
"activeTabId": state.activeTabId
|
|
73
|
+
])
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import XCTest
|
|
2
|
+
@testable import CapTabbarPlugin
|
|
3
|
+
|
|
4
|
+
class CapTabbarTests: XCTestCase {
|
|
5
|
+
func testParseTabs() {
|
|
6
|
+
let tabs = CapTabbar.parseTabs(from: [
|
|
7
|
+
["id": "home", "label": "Home"]
|
|
8
|
+
])
|
|
9
|
+
XCTAssertEqual(tabs.count, 1)
|
|
10
|
+
XCTAssertEqual(tabs[0].id, "home")
|
|
11
|
+
XCTAssertEqual(tabs[0].label, "Home")
|
|
12
|
+
}
|
|
13
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "capacitor-native-tabbar",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Native tab bar for Capacitor apps — UITabBar on iOS, BottomNavigationView on Android, overlays the WebView",
|
|
5
|
+
"main": "dist/plugin.cjs.js",
|
|
6
|
+
"module": "dist/esm/index.js",
|
|
7
|
+
"types": "dist/esm/index.d.ts",
|
|
8
|
+
"unpkg": "dist/plugin.js",
|
|
9
|
+
"files": [
|
|
10
|
+
"android/src/main/",
|
|
11
|
+
"android/build.gradle",
|
|
12
|
+
"dist/",
|
|
13
|
+
"ios/Sources",
|
|
14
|
+
"ios/Tests",
|
|
15
|
+
"Package.swift",
|
|
16
|
+
"CapacitorNativeTabbar.podspec"
|
|
17
|
+
],
|
|
18
|
+
"author": "Anton Seagull",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/AntonSeagull/capacitor-native-tabbar.git"
|
|
23
|
+
},
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/AntonSeagull/capacitor-native-tabbar/issues"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"capacitor",
|
|
29
|
+
"capacitor-plugin",
|
|
30
|
+
"ionic",
|
|
31
|
+
"native",
|
|
32
|
+
"tabbar",
|
|
33
|
+
"tab-bar",
|
|
34
|
+
"bottom-navigation",
|
|
35
|
+
"ios",
|
|
36
|
+
"android"
|
|
37
|
+
],
|
|
38
|
+
"scripts": {
|
|
39
|
+
"verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
|
|
40
|
+
"verify:ios": "xcodebuild -scheme CapacitorNativeTabbar -destination generic/platform=iOS",
|
|
41
|
+
"verify:android": "cd android && ./gradlew clean build test && cd ..",
|
|
42
|
+
"verify:web": "npm run build",
|
|
43
|
+
"lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",
|
|
44
|
+
"fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format",
|
|
45
|
+
"eslint": "eslint . --ext ts",
|
|
46
|
+
"prettier": "prettier \"**/*.{css,html,ts,js,java}\" --plugin=prettier-plugin-java",
|
|
47
|
+
"swiftlint": "node-swiftlint",
|
|
48
|
+
"docgen": "docgen --api CapTabbarPlugin --output-readme README.md --output-json dist/docs.json",
|
|
49
|
+
"build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.mjs",
|
|
50
|
+
"clean": "rimraf ./dist",
|
|
51
|
+
"watch": "tsc --watch",
|
|
52
|
+
"prepublishOnly": "npm run build"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@capacitor/android": "^8.0.0",
|
|
56
|
+
"@capacitor/core": "^8.0.0",
|
|
57
|
+
"@capacitor/docgen": "^0.3.1",
|
|
58
|
+
"@capacitor/ios": "^8.0.0",
|
|
59
|
+
"@ionic/eslint-config": "^0.4.0",
|
|
60
|
+
"@ionic/prettier-config": "^4.0.0",
|
|
61
|
+
"@ionic/swiftlint-config": "^2.0.0",
|
|
62
|
+
"eslint": "^8.57.1",
|
|
63
|
+
"prettier": "^3.6.2",
|
|
64
|
+
"prettier-plugin-java": "^2.7.7",
|
|
65
|
+
"rimraf": "^6.1.0",
|
|
66
|
+
"rollup": "^4.53.2",
|
|
67
|
+
"swiftlint": "^2.0.0",
|
|
68
|
+
"typescript": "^5.9.3"
|
|
69
|
+
},
|
|
70
|
+
"peerDependencies": {
|
|
71
|
+
"@capacitor/core": ">=8.0.0"
|
|
72
|
+
},
|
|
73
|
+
"prettier": "@ionic/prettier-config",
|
|
74
|
+
"swiftlint": "@ionic/swiftlint-config",
|
|
75
|
+
"eslintConfig": {
|
|
76
|
+
"extends": "@ionic/eslint-config/recommended"
|
|
77
|
+
},
|
|
78
|
+
"capacitor": {
|
|
79
|
+
"ios": {
|
|
80
|
+
"src": "ios"
|
|
81
|
+
},
|
|
82
|
+
"android": {
|
|
83
|
+
"src": "android"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|