@rdlabo/ionic-theme-ios27 1.2.1-beta.pr275.shad339e7688db7 → 2.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/README.md +24 -280
- package/dist/css/components/ion-toolbar.css +1 -1
- package/dist/css/ionic-theme-ios27.css +1 -1
- package/dist/css/vertical-bars.css +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/layout/index.d.ts +18 -0
- package/dist/layout/index.d.ts.map +1 -0
- package/dist/layout/index.js +26 -0
- package/dist/layout/index.js.map +1 -0
- package/dist/segment/index.js +3 -3
- package/dist/segment/index.js.map +1 -1
- package/dist/sheets-of-glass/index.js +7 -7
- package/dist/sheets-of-glass/index.js.map +1 -1
- package/dist/tab-bar-searchable/dismiss-button.js +53 -3
- package/dist/tab-bar-searchable/dismiss-button.js.map +1 -1
- package/dist/tab-bar-searchable/index.js +18 -3
- package/dist/tab-bar-searchable/index.js.map +1 -1
- package/dist/transition/ios.transition.d.ts.map +1 -1
- package/dist/transition/ios.transition.js +7 -4
- package/dist/transition/ios.transition.js.map +1 -1
- package/dist/transition/native-ui.d.ts +6 -0
- package/dist/transition/native-ui.d.ts.map +1 -0
- package/dist/{native-integration/index.js → transition/native-ui.js} +16 -30
- package/dist/transition/native-ui.js.map +1 -0
- package/docs/adaptive-themes.md +161 -0
- package/docs/add-native-ui.md +53 -0
- package/docs/animation.md +19 -3
- package/docs/eslint.md +3 -1
- package/docs/features.md +9 -5
- package/docs/iphone-duo.md +62 -35
- package/docs/migration.md +17 -26
- package/docs/setup.md +92 -0
- package/docs/special-markup.md +41 -10
- package/docs/using-ion-item-group.md +2 -2
- package/docs/vertical-bars-layout.md +67 -0
- package/package.json +12 -42
- package/src/index.ts +3 -1
- package/src/layout/index.ts +29 -0
- package/src/segment/index.ts +3 -3
- package/src/sheets-of-glass/index.ts +7 -7
- package/src/styles/components/ion-toolbar.scss +1 -1
- package/src/styles/vertical-bars.scss +15 -18
- package/src/tab-bar-searchable/dismiss-button.ts +73 -4
- package/src/tab-bar-searchable/index.ts +20 -3
- package/src/transition/ios.transition.ts +7 -4
- package/src/{native-integration/index.ts → transition/native-ui.ts} +17 -46
- package/Package.swift +0 -21
- package/dist/_virtual/_rolldown/runtime.js +0 -13
- package/dist/native/components/index.d.ts +0 -19
- package/dist/native/components/index.d.ts.map +0 -1
- package/dist/native/components/index.js +0 -40
- package/dist/native/components/index.js.map +0 -1
- package/dist/native/components/ion-back-button.d.ts +0 -9
- package/dist/native/components/ion-back-button.d.ts.map +0 -1
- package/dist/native/components/ion-back-button.js +0 -20
- package/dist/native/components/ion-back-button.js.map +0 -1
- package/dist/native/components/ion-button.d.ts +0 -10
- package/dist/native/components/ion-button.d.ts.map +0 -1
- package/dist/native/components/ion-button.js +0 -23
- package/dist/native/components/ion-button.js.map +0 -1
- package/dist/native/components/ion-buttons.d.ts +0 -11
- package/dist/native/components/ion-buttons.d.ts.map +0 -1
- package/dist/native/components/ion-buttons.js +0 -30
- package/dist/native/components/ion-buttons.js.map +0 -1
- package/dist/native/components/ion-fab.d.ts +0 -10
- package/dist/native/components/ion-fab.d.ts.map +0 -1
- package/dist/native/components/ion-fab.js +0 -132
- package/dist/native/components/ion-fab.js.map +0 -1
- package/dist/native/components/ion-menu-button.d.ts +0 -13
- package/dist/native/components/ion-menu-button.d.ts.map +0 -1
- package/dist/native/components/ion-menu-button.js +0 -31
- package/dist/native/components/ion-menu-button.js.map +0 -1
- package/dist/native/components/ion-segment.d.ts +0 -11
- package/dist/native/components/ion-segment.d.ts.map +0 -1
- package/dist/native/components/ion-segment.js +0 -30
- package/dist/native/components/ion-segment.js.map +0 -1
- package/dist/native/components/ion-tab-bar.d.ts +0 -11
- package/dist/native/components/ion-tab-bar.d.ts.map +0 -1
- package/dist/native/components/ion-tab-bar.js +0 -50
- package/dist/native/components/ion-tab-bar.js.map +0 -1
- package/dist/native/components/searchable-tabs.d.ts +0 -20
- package/dist/native/components/searchable-tabs.d.ts.map +0 -1
- package/dist/native/components/searchable-tabs.js +0 -383
- package/dist/native/components/searchable-tabs.js.map +0 -1
- package/dist/native/definitions.d.ts +0 -191
- package/dist/native/definitions.d.ts.map +0 -1
- package/dist/native/definitions.js +0 -0
- package/dist/native/foldable/fold-state.d.ts +0 -5
- package/dist/native/foldable/fold-state.d.ts.map +0 -1
- package/dist/native/foldable/fold-state.js +0 -14
- package/dist/native/foldable/fold-state.js.map +0 -1
- package/dist/native/foldable/types.d.ts +0 -12
- package/dist/native/foldable/types.d.ts.map +0 -1
- package/dist/native/foldable/types.js +0 -0
- package/dist/native/index.d.ts +0 -11
- package/dist/native/index.d.ts.map +0 -1
- package/dist/native/index.js +0 -267
- package/dist/native/index.js.map +0 -1
- package/dist/native/overlays/content.d.ts +0 -4
- package/dist/native/overlays/content.d.ts.map +0 -1
- package/dist/native/overlays/content.js +0 -51
- package/dist/native/overlays/content.js.map +0 -1
- package/dist/native/overlays/controller.d.ts +0 -8
- package/dist/native/overlays/controller.d.ts.map +0 -1
- package/dist/native/overlays/controller.js +0 -285
- package/dist/native/overlays/controller.js.map +0 -1
- package/dist/native/overlays/dialog-animation.d.ts +0 -9
- package/dist/native/overlays/dialog-animation.d.ts.map +0 -1
- package/dist/native/overlays/dialog-animation.js +0 -53
- package/dist/native/overlays/dialog-animation.js.map +0 -1
- package/dist/native/overlays/dismiss-animation.d.ts +0 -4
- package/dist/native/overlays/dismiss-animation.d.ts.map +0 -1
- package/dist/native/overlays/dismiss-animation.js +0 -18
- package/dist/native/overlays/dismiss-animation.js.map +0 -1
- package/dist/native/overlays/focus.d.ts +0 -5
- package/dist/native/overlays/focus.d.ts.map +0 -1
- package/dist/native/overlays/focus.js +0 -59
- package/dist/native/overlays/focus.js.map +0 -1
- package/dist/native/overlays/popover.d.ts +0 -12
- package/dist/native/overlays/popover.d.ts.map +0 -1
- package/dist/native/overlays/popover.js +0 -110
- package/dist/native/overlays/popover.js.map +0 -1
- package/dist/native/overlays/styles.d.ts +0 -11
- package/dist/native/overlays/styles.d.ts.map +0 -1
- package/dist/native/overlays/styles.js +0 -167
- package/dist/native/overlays/styles.js.map +0 -1
- package/dist/native/prehide.d.ts +0 -7
- package/dist/native/prehide.d.ts.map +0 -1
- package/dist/native/prehide.js +0 -230
- package/dist/native/prehide.js.map +0 -1
- package/dist/native/runtime.d.ts +0 -7
- package/dist/native/runtime.d.ts.map +0 -1
- package/dist/native/runtime.js +0 -710
- package/dist/native/runtime.js.map +0 -1
- package/dist/native/shared/bridge.d.ts +0 -6
- package/dist/native/shared/bridge.d.ts.map +0 -1
- package/dist/native/shared/bridge.js +0 -18
- package/dist/native/shared/bridge.js.map +0 -1
- package/dist/native/shared/candidate.d.ts +0 -21
- package/dist/native/shared/candidate.d.ts.map +0 -1
- package/dist/native/shared/candidate.js +0 -98
- package/dist/native/shared/candidate.js.map +0 -1
- package/dist/native/shared/crossfade.d.ts +0 -12
- package/dist/native/shared/crossfade.d.ts.map +0 -1
- package/dist/native/shared/crossfade.js +0 -67
- package/dist/native/shared/crossfade.js.map +0 -1
- package/dist/native/shared/dom.d.ts +0 -46
- package/dist/native/shared/dom.d.ts.map +0 -1
- package/dist/native/shared/dom.js +0 -172
- package/dist/native/shared/dom.js.map +0 -1
- package/dist/native/shared/events.d.ts +0 -11
- package/dist/native/shared/events.d.ts.map +0 -1
- package/dist/native/shared/events.js +0 -13
- package/dist/native/shared/events.js.map +0 -1
- package/dist/native/shared/icons.d.ts +0 -8
- package/dist/native/shared/icons.d.ts.map +0 -1
- package/dist/native/shared/icons.js +0 -61
- package/dist/native/shared/icons.js.map +0 -1
- package/dist/native/shared/modal.d.ts +0 -10
- package/dist/native/shared/modal.d.ts.map +0 -1
- package/dist/native/shared/modal.js +0 -160
- package/dist/native/shared/modal.js.map +0 -1
- package/dist/native/shared/projection-id.d.ts +0 -4
- package/dist/native/shared/projection-id.d.ts.map +0 -1
- package/dist/native/shared/projection-id.js +0 -7
- package/dist/native/shared/projection-id.js.map +0 -1
- package/dist/native/shared/vertical-bars-geometry.d.ts +0 -10
- package/dist/native/shared/vertical-bars-geometry.d.ts.map +0 -1
- package/dist/native/shared/vertical-bars-geometry.js +0 -85
- package/dist/native/shared/vertical-bars-geometry.js.map +0 -1
- package/dist/native/vertical-bars-web-search.d.ts +0 -10
- package/dist/native/vertical-bars-web-search.d.ts.map +0 -1
- package/dist/native/vertical-bars-web-search.js +0 -90
- package/dist/native/vertical-bars-web-search.js.map +0 -1
- package/dist/native/vertical-bars-web.d.ts +0 -5
- package/dist/native/vertical-bars-web.d.ts.map +0 -1
- package/dist/native/vertical-bars-web.js +0 -411
- package/dist/native/vertical-bars-web.js.map +0 -1
- package/dist/native-integration/index.d.ts +0 -23
- package/dist/native-integration/index.d.ts.map +0 -1
- package/dist/native-integration/index.js.map +0 -1
- package/dist/native-integration/transition.d.ts +0 -5
- package/dist/native-integration/transition.d.ts.map +0 -1
- package/dist/native-integration/transition.js +0 -16
- package/dist/native-integration/transition.js.map +0 -1
- package/dist/vertical-bars.d.ts +0 -5
- package/dist/vertical-bars.js +0 -4
- package/docs/e2e-testing.md +0 -51
- package/docs/feedback.md +0 -65
- package/docs/iphone-duo-with-original-theme.md +0 -158
- package/docs/native-ui-shell.md +0 -325
- package/docs/vertical-bars.md +0 -300
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellButton.swift +0 -71
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellButtons.swift +0 -24
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellComponents.swift +0 -18
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellFab.swift +0 -80
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellSearchController.swift +0 -432
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellSegment.swift +0 -91
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellTabBar.swift +0 -218
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellVerticalBars.swift +0 -565
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellVerticalSearch.swift +0 -118
- package/ios/Sources/IonicNativeUIShellPlugin/IonicNativeUIShellPlugin.swift +0 -502
- package/ios/Sources/IonicNativeUIShellPlugin/Overlays/ShellOverlayController.swift +0 -119
- package/ios/Sources/IonicNativeUIShellPlugin/Overlays/ShellOverlayHost.swift +0 -75
- package/ios/Sources/IonicNativeUIShellPlugin/Shared/ShellCrossfade.swift +0 -35
- package/ios/Sources/IonicNativeUIShellPlugin/Shared/ShellHost.swift +0 -8
- package/ios/Sources/IonicNativeUIShellPlugin/Shared/ShellRendering.swift +0 -27
- package/ios/Sources/IonicNativeUIShellPlugin/Shared/ShellSnapshot.swift +0 -135
- package/ios/Sources/IonicNativeUIShellPlugin/Shared/ShellVerticalBarsGeometry.swift +0 -52
- package/ios/Tests/IonicNativeUIShellPluginTests/ShellSnapshotTests.swift +0 -788
- package/src/native/components/index.ts +0 -53
- package/src/native/components/ion-back-button.ts +0 -19
- package/src/native/components/ion-button.ts +0 -24
- package/src/native/components/ion-buttons.ts +0 -38
- package/src/native/components/ion-fab.ts +0 -151
- package/src/native/components/ion-menu-button.ts +0 -24
- package/src/native/components/ion-segment.ts +0 -27
- package/src/native/components/ion-tab-bar.ts +0 -50
- package/src/native/components/searchable-tabs.ts +0 -459
- package/src/native/definitions.ts +0 -218
- package/src/native/foldable/fold-state.ts +0 -9
- package/src/native/foldable/types.ts +0 -4
- package/src/native/index.ts +0 -315
- package/src/native/overlays/content.ts +0 -51
- package/src/native/overlays/controller.ts +0 -349
- package/src/native/overlays/dialog-animation.ts +0 -53
- package/src/native/overlays/dismiss-animation.ts +0 -15
- package/src/native/overlays/focus.ts +0 -64
- package/src/native/overlays/popover.ts +0 -120
- package/src/native/overlays/styles.ts +0 -181
- package/src/native/prehide.ts +0 -265
- package/src/native/runtime.ts +0 -885
- package/src/native/shared/bridge.ts +0 -18
- package/src/native/shared/candidate.ts +0 -150
- package/src/native/shared/crossfade.ts +0 -67
- package/src/native/shared/dom.ts +0 -286
- package/src/native/shared/events.ts +0 -8
- package/src/native/shared/icons.ts +0 -57
- package/src/native/shared/modal.ts +0 -157
- package/src/native/shared/projection-id.ts +0 -2
- package/src/native/shared/vertical-bars-geometry.ts +0 -80
- package/src/native/vertical-bars-web-search.ts +0 -86
- package/src/native/vertical-bars-web.ts +0 -485
- package/src/native-integration/transition.ts +0 -15
- package/src/vertical-bars.ts +0 -11
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import UIKit
|
|
2
|
-
import WebKit
|
|
3
|
-
|
|
4
|
-
/// Hosts only the live content of a Vertical Bars popover.
|
|
5
|
-
final class ShellOverlayHost: UIViewController {
|
|
6
|
-
let webView: WKWebView
|
|
7
|
-
private(set) var options: [String: Any]?
|
|
8
|
-
let event: (String) -> Void
|
|
9
|
-
// Settle native presentation before the JS bridge's five-second deadline.
|
|
10
|
-
static let completionTimeout: TimeInterval = 4
|
|
11
|
-
var dismissAnchored: (() -> Void)?
|
|
12
|
-
var cancelAnchored: (() -> Void)?
|
|
13
|
-
var anchoredVisible = false
|
|
14
|
-
private var dismissalCompletions: [() -> Void]?
|
|
15
|
-
private var dismissalTimeout: DispatchWorkItem?
|
|
16
|
-
|
|
17
|
-
func completeDismissal() {
|
|
18
|
-
dismissalTimeout?.cancel()
|
|
19
|
-
dismissalTimeout = nil
|
|
20
|
-
anchoredVisible = false
|
|
21
|
-
let completions = dismissalCompletions ?? []
|
|
22
|
-
dismissalCompletions = nil
|
|
23
|
-
completions.forEach { $0() }
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Dismiss, close and stop may overlap while SwiftUI is still disappearing.
|
|
27
|
-
// Start dismissal once and deliver its completion to every waiting caller.
|
|
28
|
-
func dismissPopover(animated: Bool, completion: @escaping () -> Void) {
|
|
29
|
-
if dismissalCompletions != nil {
|
|
30
|
-
dismissalCompletions?.append(completion)
|
|
31
|
-
return
|
|
32
|
-
}
|
|
33
|
-
dismissalCompletions = [completion]
|
|
34
|
-
let finish = { [self] in completeDismissal() }
|
|
35
|
-
let timeout = DispatchWorkItem { [weak self] in
|
|
36
|
-
guard let self, self.dismissalCompletions != nil else { return }
|
|
37
|
-
// Remove the owning rail if SwiftUI never reports disappearance.
|
|
38
|
-
// Its detach path settles waiters before any replacement can attach.
|
|
39
|
-
self.cancelAnchored?()
|
|
40
|
-
self.completeDismissal()
|
|
41
|
-
}
|
|
42
|
-
dismissalTimeout = timeout
|
|
43
|
-
DispatchQueue.main.asyncAfter(deadline: .now() + Self.completionTimeout, execute: timeout)
|
|
44
|
-
if let dismissAnchored {
|
|
45
|
-
if anchoredVisible { dismissAnchored() }
|
|
46
|
-
else { dismissAnchored(); finish() }
|
|
47
|
-
} else if presentingViewController != nil {
|
|
48
|
-
dismiss(animated: animated, completion: finish)
|
|
49
|
-
} else { finish() }
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
init(configuration: WKWebViewConfiguration, options: [String: Any]?, event: @escaping (String) -> Void) {
|
|
53
|
-
webView = WKWebView(frame: .zero, configuration: configuration)
|
|
54
|
-
self.event = event
|
|
55
|
-
super.init(nibName: nil, bundle: nil)
|
|
56
|
-
apply(options)
|
|
57
|
-
}
|
|
58
|
-
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
|
59
|
-
|
|
60
|
-
func apply(_ options: [String: Any]?) {
|
|
61
|
-
guard let options else { return }
|
|
62
|
-
self.options = options
|
|
63
|
-
preferredContentSize = CGSize(width: options["width"] as? Double ?? 280, height: options["height"] as? Double ?? 200)
|
|
64
|
-
overrideUserInterfaceStyle = options["dark"] as? Bool == true ? .dark : .light
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
override func loadView() {
|
|
68
|
-
webView.isOpaque = false
|
|
69
|
-
webView.backgroundColor = .clear
|
|
70
|
-
webView.scrollView.backgroundColor = .clear
|
|
71
|
-
webView.scrollView.contentInsetAdjustmentBehavior = .never
|
|
72
|
-
view = webView
|
|
73
|
-
view.accessibilityViewIsModal = true
|
|
74
|
-
}
|
|
75
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import UIKit
|
|
2
|
-
|
|
3
|
-
@MainActor enum ShellCrossfade {
|
|
4
|
-
static func duration(_ milliseconds: Double?) -> TimeInterval {
|
|
5
|
-
UIAccessibility.isReduceMotionEnabled ? 0 : (milliseconds ?? 0) / 1000
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
static func enter(_ view: UIView, duration: TimeInterval) {
|
|
9
|
-
guard duration > 0 else { return }
|
|
10
|
-
view.alpha = 0
|
|
11
|
-
UIView.animate(withDuration: duration, delay: 0, options: [.curveEaseInOut, .allowUserInteraction]) {
|
|
12
|
-
view.alpha = 1
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
static func retire(_ view: UIView, duration: TimeInterval) {
|
|
17
|
-
// The outgoing copy is visual only: it cannot keep accepting actions or
|
|
18
|
-
// VoiceOver focus once the Web source has been restored.
|
|
19
|
-
if duration > 0, let host = view.superview, let parent = host.superview,
|
|
20
|
-
!view.isHidden, !host.isHidden, let copy = view.snapshotView(afterScreenUpdates: false) {
|
|
21
|
-
copy.frame = host.convert(view.frame, to: parent)
|
|
22
|
-
copy.alpha = CGFloat(view.layer.presentation()?.opacity ?? Float(view.alpha))
|
|
23
|
-
copy.isUserInteractionEnabled = false
|
|
24
|
-
copy.isAccessibilityElement = false
|
|
25
|
-
copy.accessibilityElementsHidden = true
|
|
26
|
-
parent.insertSubview(copy, aboveSubview: host)
|
|
27
|
-
UIView.animate(withDuration: duration, delay: 0, options: [.curveEaseInOut]) {
|
|
28
|
-
copy.alpha = 0
|
|
29
|
-
} completion: { _ in
|
|
30
|
-
copy.removeFromSuperview()
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
view.removeFromSuperview()
|
|
34
|
-
}
|
|
35
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import UIKit
|
|
2
|
-
|
|
3
|
-
final class ShellRendering {
|
|
4
|
-
private var images: [String: UIImage] = [:]
|
|
5
|
-
func clear() { images.removeAll() }
|
|
6
|
-
|
|
7
|
-
func image(_ item: ShellItemContent) -> UIImage? {
|
|
8
|
-
guard let data = item.icon, let width = item.iconWidth, width > 0 else { return nil }
|
|
9
|
-
let template = item.iconTemplate == true
|
|
10
|
-
let key = (template ? "template:" : "original:") + data
|
|
11
|
-
if let cached = images[key] { return cached }
|
|
12
|
-
guard let bytes = Data(base64Encoded: data), let raw = UIImage(data: bytes), let cg = raw.cgImage else { return nil }
|
|
13
|
-
let result = UIImage(cgImage: cg, scale: CGFloat(cg.width) / width, orientation: .up)
|
|
14
|
-
.withRenderingMode(template ? .alwaysTemplate : .alwaysOriginal)
|
|
15
|
-
if images.count >= 128 { images.removeAll() }
|
|
16
|
-
images[key] = result
|
|
17
|
-
return result
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
func color(_ css: String?) -> UIColor {
|
|
21
|
-
guard let css else { return .label }
|
|
22
|
-
let numbers = css.components(separatedBy: CharacterSet(charactersIn: "0123456789.").inverted).compactMap(Double.init)
|
|
23
|
-
guard numbers.count >= 3 else { return .label }
|
|
24
|
-
return UIColor(red: numbers[0] / 255, green: numbers[1] / 255, blue: numbers[2] / 255,
|
|
25
|
-
alpha: numbers.count > 3 ? numbers[3] : 1)
|
|
26
|
-
}
|
|
27
|
-
}
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import Foundation
|
|
2
|
-
import CoreGraphics
|
|
3
|
-
|
|
4
|
-
enum ShellComponent: String, Decodable {
|
|
5
|
-
case button = "ion-button", buttons = "ion-buttons"
|
|
6
|
-
case backButton = "ion-back-button", menuButton = "ion-menu-button"
|
|
7
|
-
case tabBar = "ion-tab-bar", segment = "ion-segment", fab = "ion-fab"
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
struct ShellSnapshot: Decodable {
|
|
11
|
-
let revision: Int
|
|
12
|
-
let transitionDuration: Double?
|
|
13
|
-
let viewportWidth: Double
|
|
14
|
-
let verticalBarFrame: ShellFrame?
|
|
15
|
-
let verticalBarEdge: String?
|
|
16
|
-
let controls: [ShellControl]
|
|
17
|
-
|
|
18
|
-
var isValid: Bool {
|
|
19
|
-
revision >= 0 && (transitionDuration.map { $0.isFinite && $0 >= 0 && $0 <= 500 } ?? true) && viewportWidth.isFinite && viewportWidth > 0 && controls.count <= 100 &&
|
|
20
|
-
(verticalBarFrame.map { $0.isValid } ?? true) &&
|
|
21
|
-
(verticalBarEdge == nil || verticalBarEdge == "left" || verticalBarEdge == "right") &&
|
|
22
|
-
Set(controls.map(\.id)).count == controls.count && controls.allSatisfy(\.isValid)
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Frames and content decode from the same flat wire object. Keeping them separate
|
|
27
|
-
// lets FABs compare artwork without treating movement or visibility as new content.
|
|
28
|
-
struct ShellFrame: Decodable, Equatable {
|
|
29
|
-
let x: Double
|
|
30
|
-
let y: Double
|
|
31
|
-
let width: Double
|
|
32
|
-
let height: Double
|
|
33
|
-
var rect: CGRect { CGRect(x: x, y: y, width: width, height: height) }
|
|
34
|
-
var isValid: Bool { [x, y, width, height].allSatisfy(\.isFinite) && width > 0 && height > 0 }
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
struct ShellControl: Decodable, Equatable {
|
|
38
|
-
enum Placement: String, Decodable { case verticalBars = "vertical-bars" }
|
|
39
|
-
enum ToolbarSlot: String, Decodable { case start, end }
|
|
40
|
-
let id: String
|
|
41
|
-
let kind: ShellComponent
|
|
42
|
-
let placement: Placement?
|
|
43
|
-
let toolbarSlot: ToolbarSlot?
|
|
44
|
-
let frame: ShellFrame
|
|
45
|
-
let items: [ShellItem]
|
|
46
|
-
let dark: Bool
|
|
47
|
-
let rtl: Bool
|
|
48
|
-
let tabBarAnchor: ShellTabBar.Anchor?
|
|
49
|
-
let search: ShellSearch?
|
|
50
|
-
|
|
51
|
-
private enum CodingKeys: String, CodingKey { case id, kind, placement, toolbarSlot, items, dark, rtl, tabBarAnchor, search }
|
|
52
|
-
|
|
53
|
-
init(from decoder: Decoder) throws {
|
|
54
|
-
let values = try decoder.container(keyedBy: CodingKeys.self)
|
|
55
|
-
id = try values.decode(String.self, forKey: .id)
|
|
56
|
-
kind = try values.decode(ShellComponent.self, forKey: .kind)
|
|
57
|
-
placement = try values.decodeIfPresent(Placement.self, forKey: .placement)
|
|
58
|
-
toolbarSlot = try values.decodeIfPresent(ToolbarSlot.self, forKey: .toolbarSlot)
|
|
59
|
-
frame = try ShellFrame(from: decoder)
|
|
60
|
-
items = try values.decode([ShellItem].self, forKey: .items)
|
|
61
|
-
dark = try values.decode(Bool.self, forKey: .dark)
|
|
62
|
-
rtl = try values.decode(Bool.self, forKey: .rtl)
|
|
63
|
-
tabBarAnchor = try values.decodeIfPresent(ShellTabBar.Anchor.self, forKey: .tabBarAnchor)
|
|
64
|
-
search = try values.decodeIfPresent(ShellSearch.self, forKey: .search)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
var isValid: Bool {
|
|
68
|
-
ShellComponents.supported.contains(kind) && !id.isEmpty && frame.isValid && !items.isEmpty && items.count <= 30 &&
|
|
69
|
-
(!ShellButton.kinds.contains(kind) || items.count == 1) &&
|
|
70
|
-
(toolbarSlot == nil || (placement == .verticalBars && [.button, .buttons, .menuButton].contains(kind))) &&
|
|
71
|
-
Set(items.map(\.id)).count == items.count && items.allSatisfy(\.isValid) &&
|
|
72
|
-
(tabBarAnchor.map { kind == .tabBar && $0.isValid } ?? true) &&
|
|
73
|
-
(search.map { kind == .tabBar && $0.isValid } ?? true)
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
struct ShellItem: Decodable, Equatable {
|
|
78
|
-
let frame: ShellFrame
|
|
79
|
-
let content: ShellItemContent
|
|
80
|
-
let visible: Bool?
|
|
81
|
-
var id: String { content.id }
|
|
82
|
-
|
|
83
|
-
private enum CodingKeys: String, CodingKey { case visible }
|
|
84
|
-
|
|
85
|
-
init(from decoder: Decoder) throws {
|
|
86
|
-
frame = try ShellFrame(from: decoder)
|
|
87
|
-
content = try ShellItemContent(from: decoder)
|
|
88
|
-
visible = try decoder.container(keyedBy: CodingKeys.self).decodeIfPresent(Bool.self, forKey: .visible)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
var isValid: Bool { frame.isValid && content.isValid }
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
struct ShellBadge: Decodable, Equatable {
|
|
95
|
-
let value: String
|
|
96
|
-
let color: String
|
|
97
|
-
let textColor: String
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
struct ShellItemContent: Decodable, Equatable {
|
|
101
|
-
enum IconPosition: String, Decodable { case leading, trailing, top }
|
|
102
|
-
enum ButtonFill: String, Decodable { case clear, solid, outline }
|
|
103
|
-
let id: String
|
|
104
|
-
var label: String
|
|
105
|
-
let accessibilityLabel: String
|
|
106
|
-
let disabled: Bool
|
|
107
|
-
let selected: Bool
|
|
108
|
-
let fontSize: Double
|
|
109
|
-
let fontWeight: Double
|
|
110
|
-
let color: String
|
|
111
|
-
let buttonFill: ButtonFill?
|
|
112
|
-
let backgroundColor: String?
|
|
113
|
-
let borderColor: String?
|
|
114
|
-
let borderWidth: CGFloat?
|
|
115
|
-
let badge: ShellBadge?
|
|
116
|
-
var icon: String?
|
|
117
|
-
var iconWidth: Double?
|
|
118
|
-
var iconHeight: Double?
|
|
119
|
-
let iconPosition: IconPosition?
|
|
120
|
-
let imagePadding: Double?
|
|
121
|
-
let contentInsetLeading: Double?
|
|
122
|
-
let contentInsetTrailing: Double?
|
|
123
|
-
let iconTemplate: Bool?
|
|
124
|
-
let closeIcon: String?
|
|
125
|
-
let closeIconWidth: Double?
|
|
126
|
-
let closeIconHeight: Double?
|
|
127
|
-
let iconTransition: Double?
|
|
128
|
-
|
|
129
|
-
var isValid: Bool {
|
|
130
|
-
!id.isEmpty && fontSize.isFinite && fontSize >= 0 && fontWeight.isFinite &&
|
|
131
|
-
[iconWidth, iconHeight, closeIconWidth, closeIconHeight].compactMap { $0 }.allSatisfy { $0.isFinite && $0 > 0 } &&
|
|
132
|
-
[imagePadding, contentInsetLeading, contentInsetTrailing].compactMap { $0 }.allSatisfy { $0.isFinite } &&
|
|
133
|
-
(iconTransition.map { $0.isFinite && $0 >= 0 } ?? true)
|
|
134
|
-
}
|
|
135
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import UIKit
|
|
2
|
-
|
|
3
|
-
/// Reads public accessibility geometry after the system has laid out its rail.
|
|
4
|
-
/// IDs bind measurements to current DOM controls without private class names.
|
|
5
|
-
/// Compact square ancestors approximate the system button surface; this remains
|
|
6
|
-
/// a layout heuristic and invalid measurements fall back to Web CSS.
|
|
7
|
-
enum ShellVerticalBarsGeometry {
|
|
8
|
-
static func measure(in root: UIView, relativeTo webView: UIView, ids: Set<String>) -> [String: CGRect] {
|
|
9
|
-
guard let window = root.window, window === webView.window else { return [:] }
|
|
10
|
-
root.layoutIfNeeded()
|
|
11
|
-
var result: [String: CGRect] = [:]
|
|
12
|
-
var visited = Set<ObjectIdentifier>()
|
|
13
|
-
func visit(_ object: NSObject, depth: Int) {
|
|
14
|
-
guard depth < 40, visited.count < 2000, visited.insert(ObjectIdentifier(object)).inserted else { return }
|
|
15
|
-
if let id = (object as? UIView)?.accessibilityIdentifier ?? (object as? UIAccessibilityElement)?.accessibilityIdentifier, ids.contains(id) {
|
|
16
|
-
let screenFrame = object.accessibilityFrame
|
|
17
|
-
let frame = (object as? UIView).map { view -> CGRect in
|
|
18
|
-
var surface = view
|
|
19
|
-
var ancestor = view.superview
|
|
20
|
-
while let current = ancestor, current !== root,
|
|
21
|
-
current.bounds.width < 100, current.bounds.height < 100 {
|
|
22
|
-
if current.bounds.width >= max(surface.bounds.width, 1), current.bounds.height >= max(surface.bounds.height, 1),
|
|
23
|
-
abs(current.bounds.width - current.bounds.height) < 1 { surface = current }
|
|
24
|
-
ancestor = current.superview
|
|
25
|
-
}
|
|
26
|
-
return surface.convert(surface.bounds, to: webView)
|
|
27
|
-
} ?? webView.convert(screenFrame, from: window.screen.coordinateSpace)
|
|
28
|
-
if !frame.isNull, !frame.isInfinite, frame.width > 0, frame.height > 0,
|
|
29
|
-
webView.bounds.intersects(frame), frame.width < webView.bounds.width * 0.75 {
|
|
30
|
-
// UIKit may expose a label and its larger button with the same ID.
|
|
31
|
-
if result[id] == nil || frame.width * frame.height > result[id]!.width * result[id]!.height {
|
|
32
|
-
result[id] = frame
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
if let view = object as? UIView { view.subviews.forEach { visit($0, depth: depth + 1) } }
|
|
37
|
-
if object === webView { return }
|
|
38
|
-
if let elements = object.accessibilityElements {
|
|
39
|
-
elements.compactMap { $0 as? NSObject }.forEach { visit($0, depth: depth + 1) }
|
|
40
|
-
} else {
|
|
41
|
-
let count = object.accessibilityElementCount()
|
|
42
|
-
if count > 0 && count < 200 {
|
|
43
|
-
for index in 0..<count {
|
|
44
|
-
if let element = object.accessibilityElement(at: index) as? NSObject { visit(element, depth: depth + 1) }
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
visit(root, depth: 0)
|
|
50
|
-
return result
|
|
51
|
-
}
|
|
52
|
-
}
|