@momo-kits/native-kits 0.162.2-beta.26 → 0.162.2-beta.27
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/CLAUDE.md +1 -1
- package/ios/Application/ApplicationEnvironment.swift +0 -11
- package/ios/Application/Components.swift +44 -34
- package/ios/Application/FloatingButton.swift +9 -10
- package/ios/Application/HeaderRight.swift +45 -79
- package/ios/Application/Screen.swift +1 -2
- package/ios/Badge/Badge.swift +14 -11
- package/ios/Badge/BadgeRibbon.swift +21 -12
- package/ios/Button/Button.swift +22 -61
- package/ios/Checkbox/Checkbox.swift +13 -14
- package/ios/Chip/Chip.swift +9 -23
- package/ios/Colors+Radius+Spacing/Colors.swift +12 -12
- package/ios/Extensions/Color++.swift +0 -32
- package/ios/Icon/Icon.swift +1 -6
- package/ios/Input/Input.swift +25 -41
- package/ios/Input/InputPhoneNumber.swift +19 -20
- package/ios/Input/InputSearch.swift +47 -100
- package/ios/Input/InputTextArea.swift +40 -66
- package/ios/Popup/PopupDisplay.swift +16 -8
- package/ios/Popup/PopupInput.swift +8 -2
- package/ios/Swipeable/SwipeCell.swift +0 -185
- package/ios/Swipeable/SwipeCellModel.swift +0 -21
- package/ios/Typography/Text.swift +5 -79
- package/ios/Typography/Typography.swift +0 -3
- package/ios/native-kits.podspec +8 -61
- package/package.json +1 -1
- package/publish.sh +0 -50
- package/.claude/settings.local.json +0 -39
- package/ios/Application/Navigation/BottomTab/BottomTab.swift +0 -103
- package/ios/Application/Navigation/BottomTab/BottomTabBar.swift +0 -215
- package/ios/Application/Navigation/BottomTab/CurvedContainer.swift +0 -65
- package/ios/Application/Navigation/HeaderBackProps.swift +0 -66
- package/ios/Application/Navigation/HeaderTitle.swift +0 -57
- package/ios/Application/Navigation/HeaderUser.swift +0 -209
- package/ios/Application/Navigation/NavigationContainer.swift +0 -176
- package/ios/Application/Navigation/NavigationOptions.swift +0 -109
- package/ios/Application/Navigation/Navigator.swift +0 -364
- package/ios/Application/Navigation/Overplay/BottomSheet.swift +0 -188
- package/ios/Application/Navigation/Overplay/ModalScreen.swift +0 -68
- package/ios/Application/Navigation/Overplay/SnackBar.swift +0 -144
- package/ios/Application/Navigation/component/Header.swift +0 -178
- package/ios/Application/Navigation/component/HeaderBackground.swift +0 -83
- package/ios/Application/Navigation/component/HeaderColor.swift +0 -65
- package/ios/Application/StackScreen.swift +0 -453
- package/ios/Avatar/Avatar.swift +0 -193
- package/ios/BaselineView/BaselineView.swift +0 -163
- package/ios/Carousel/Carousel.swift +0 -181
- package/ios/Collapse/Collapse.swift +0 -182
- package/ios/Colors+Radius+Spacing/Theme.swift +0 -295
- package/ios/DateTimePicker/DateTimePicker.swift +0 -210
- package/ios/DateTimePicker/DateTimePickerTypes.swift +0 -55
- package/ios/DateTimePicker/DateTimePickerUtils.swift +0 -167
- package/ios/DateTimePicker/WheelPicker.swift +0 -161
- package/ios/Divider/Divider.swift +0 -16
- package/ios/IconButton/IconButton.swift +0 -112
- package/ios/InputDropDown/InputDropDown.swift +0 -198
- package/ios/InputMoney/InputMoney.swift +0 -335
- package/ios/InputOTP/InputOTP.swift +0 -210
- package/ios/Loader/Loader.swift +0 -113
- package/ios/MoMoUIKits.podspec +0 -5
- package/ios/Pagination/PaginationDot.swift +0 -61
- package/ios/Pagination/PaginationNumber.swift +0 -35
- package/ios/Pagination/PaginationScroll.swift +0 -101
- package/ios/Pagination/PaginationWhiteDot.swift +0 -37
- package/ios/Popup/PopupNotify.swift +0 -219
- package/ios/ProgressInfo/ProgressInfo.swift +0 -294
- package/ios/Radio/Radio.swift +0 -70
- package/ios/Rating/Rating.swift +0 -82
- package/ios/Resources/MoMoUIKitsColors.xcassets/AccentColor.colorset/Contents.json +0 -11
- package/ios/Resources/MoMoUIKitsColors.xcassets/Background.colorset/Contents.json +0 -38
- package/ios/Resources/MoMoUIKitsColors.xcassets/Border.colorset/Contents.json +0 -38
- package/ios/Resources/MoMoUIKitsColors.xcassets/Card.colorset/Contents.json +0 -38
- package/ios/Resources/MoMoUIKitsColors.xcassets/Contents.json +0 -6
- package/ios/Resources/MoMoUIKitsColors.xcassets/Error.colorset/Contents.json +0 -38
- package/ios/Resources/MoMoUIKitsColors.xcassets/Primary.colorset/Contents.json +0 -38
- package/ios/Resources/MoMoUIKitsColors.xcassets/PrimaryDark.colorset/Contents.json +0 -38
- package/ios/Resources/MoMoUIKitsColors.xcassets/PrimaryLight.colorset/Contents.json +0 -38
- package/ios/Resources/MoMoUIKitsColors.xcassets/Secondary.colorset/Contents.json +0 -38
- package/ios/Resources/MoMoUIKitsColors.xcassets/Success.colorset/Contents.json +0 -38
- package/ios/Resources/MoMoUIKitsColors.xcassets/Text.colorset/Contents.json +0 -38
- package/ios/Resources/MoMoUIKitsColors.xcassets/TextSecondary.colorset/Contents.json +0 -38
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/Contents.json +0 -9
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-default.colorset/Contents.json +0 -38
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-disable.colorset/Contents.json +0 -38
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-pressed.colorset/Contents.json +0 -38
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-selected.colorset/Contents.json +0 -38
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-surface.colorset/Contents.json +0 -38
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-tonal.colorset/Contents.json +0 -38
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/border-default.colorset/Contents.json +0 -38
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/border-disable.colorset/Contents.json +0 -38
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/gradient.colorset/Contents.json +0 -38
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/primary.colorset/Contents.json +0 -38
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/secondary.colorset/Contents.json +0 -38
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-container.colorset/Contents.json +0 -38
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-primary.colorset/Contents.json +0 -38
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-secondary.colorset/Contents.json +0 -38
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-default.colorset/Contents.json +0 -38
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-disable.colorset/Contents.json +0 -38
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-hint.colorset/Contents.json +0 -38
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-secondary.colorset/Contents.json +0 -38
- package/ios/Resources/MoMoUIKitsColors.xcassets/Warning.colorset/Contents.json +0 -38
- package/ios/Skeleton/Skeleton.swift +0 -99
- package/ios/Slider/Slider.swift +0 -237
- package/ios/Stepper/Stepper.swift +0 -220
- package/ios/Steps/Steps.swift +0 -451
- package/ios/SuggestAction/SuggestAction.swift +0 -97
- package/ios/TabView/TabView.swift +0 -530
- package/ios/Tag/Tag.swift +0 -98
- package/ios/Title/Title.swift +0 -269
- package/ios/Tooltip/Tooltip.swift +0 -482
- package/ios/Typography/FontScaleStore.swift +0 -19
- package/ios/Uploader/Uploader.swift +0 -183
- package/ios-demo/MoMoUIKitsDemo.podspec +0 -20
- package/ios-demo/Resources/dot_loading.json +0 -558
- package/ios-demo/Resources/icon.json +0 -4052
- package/ios-demo/Resources/lottie_circle_loader.json +0 -1
- package/ios-demo/Sources/MoMoUIKitsDemo/DemoScaffold.swift +0 -48
- package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsBadgeDemoView.swift +0 -25
- package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsButtonDemoView.swift +0 -26
- package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsCheckboxDemoView.swift +0 -19
- package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsChipDemoView.swift +0 -33
- package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsInputDemoView.swift +0 -48
- package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsPopupDemoView.swift +0 -21
- package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsRadioDemoView.swift +0 -17
- package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsTypographyDemoView.swift +0 -28
- package/ios-demo/Sources/MoMoUIKitsDemo/MoMoUIKitsDemoContext.swift +0 -31
- package/ios-demo/Sources/MoMoUIKitsDemo/MoMoUIKitsDemoFactory.swift +0 -24
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AnimatedHeaderDemo.swift +0 -110
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AvatarDemo.swift +0 -93
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BadgeDemo.swift +0 -269
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BaselineView.swift +0 -117
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BottomTabDemo.swift +0 -89
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ButtonDemo.swift +0 -100
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CarouselDemo.swift +0 -126
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CheckboxDemo.swift +0 -63
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ChipDemo.swift +0 -20
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CollapseDemo.swift +0 -93
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ContentView.swift +0 -107
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DateTimePickerDemo.swift +0 -124
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DividerDemo.swift +0 -14
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/HeaderUserDemo.swift +0 -49
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +0 -278
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IconButtonDemo.swift +0 -58
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ImageDemo.swift +0 -78
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InformationDemo.swift +0 -138
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDemo.swift +0 -111
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDropDownDemo.swift +0 -136
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputMoneyDemo.swift +0 -107
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputOTPDemo.swift +0 -73
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputPhoneNumberDemo.swift +0 -100
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputSearchDemo.swift +0 -32
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputTextAreaDemo.swift +0 -57
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LoaderDemo.swift +0 -70
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +0 -227
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PaginationDemo.swift +0 -72
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupDisplayDemo.swift +0 -175
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupNotifyDemo.swift +0 -121
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupPromotionDemo.swift +0 -76
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ProgressInfoDemo.swift +0 -89
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RadioDemo.swift +0 -32
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RatingDemo.swift +0 -56
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SkeletonDemo.swift +0 -32
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SliderDemo.swift +0 -150
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SnackBarDemo.swift +0 -138
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepperDemo.swift +0 -103
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepsDemo.swift +0 -94
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SuggestActionDemo.swift +0 -79
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwipeDemo.swift +0 -152
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwitchDemo.swift +0 -33
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TabViewDemo.swift +0 -190
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TagDemo.swift +0 -34
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ThemeDemo.swift +0 -182
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TitleDemo.swift +0 -109
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TooltipDemo.swift +0 -187
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TypographyDemo.swift +0 -87
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/UploaderDemo.swift +0 -131
- package/ios-demo/Sources/MoMoUIKitsDemo/SwiftUIDemoBrowserView.swift +0 -51
- package/ios-demo/Sources/MoMoUIKitsDemo/UIKitsDemoModels.swift +0 -15
- package/ios-demo/Sources/MoMoUIKitsDemo/UIKitsDemoRegistry.swift +0 -73
- package/scripts/gen-ios-color.mjs +0 -136
package/ios/Avatar/Avatar.swift
DELETED
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
import SwiftUI
|
|
2
|
-
|
|
3
|
-
let LOGO_MOMO_URL =
|
|
4
|
-
"https://static.momocdn.net/app/img/kits/logo_momo_circle.png"
|
|
5
|
-
let ICON_SUPPORT_DEFAULT_URL =
|
|
6
|
-
"https://static.momocdn.net/app/img/kits/_indicator.png"
|
|
7
|
-
|
|
8
|
-
public enum AvatarSize {
|
|
9
|
-
case size24
|
|
10
|
-
case size32
|
|
11
|
-
case size40
|
|
12
|
-
case size48
|
|
13
|
-
case size56
|
|
14
|
-
case size72
|
|
15
|
-
|
|
16
|
-
var sizeDp: CGFloat {
|
|
17
|
-
switch self {
|
|
18
|
-
case .size24: return 24
|
|
19
|
-
case .size32: return 32
|
|
20
|
-
case .size40: return 40
|
|
21
|
-
case .size48: return 48
|
|
22
|
-
case .size56: return 56
|
|
23
|
-
case .size72: return 72
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
var iconSizeDp: CGFloat {
|
|
28
|
-
switch self {
|
|
29
|
-
case .size24: return 16
|
|
30
|
-
case .size32: return 16
|
|
31
|
-
case .size40: return 24
|
|
32
|
-
case .size48: return 32
|
|
33
|
-
case .size56: return 32
|
|
34
|
-
case .size72: return 40
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
public struct Avatar: View {
|
|
40
|
-
@Environment(\.appTheme) private var theme
|
|
41
|
-
|
|
42
|
-
// MARK: Lifecycle
|
|
43
|
-
|
|
44
|
-
public init(
|
|
45
|
-
size: AvatarSize = .size32,
|
|
46
|
-
rounded: Bool = true,
|
|
47
|
-
name: String? = nil,
|
|
48
|
-
image: String? = nil,
|
|
49
|
-
showIconMomo: Bool = false,
|
|
50
|
-
showIconSupport: Bool = false,
|
|
51
|
-
iconSupport: String = "https://static.momocdn.net/app/img/kits/_indicator.png",
|
|
52
|
-
accessibilityLabel: String? = nil
|
|
53
|
-
) {
|
|
54
|
-
self.size = size
|
|
55
|
-
self.rounded = rounded
|
|
56
|
-
self.name = name
|
|
57
|
-
self.image = image
|
|
58
|
-
self.showIconMomo = showIconMomo
|
|
59
|
-
self.showIconSupport = showIconSupport
|
|
60
|
-
self.iconSupport = iconSupport
|
|
61
|
-
self.accessibilityLabel = accessibilityLabel
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// MARK: Public
|
|
65
|
-
|
|
66
|
-
public var body: some View {
|
|
67
|
-
ZStack {
|
|
68
|
-
// Inner content clipped by shape with border
|
|
69
|
-
ZStack {
|
|
70
|
-
content
|
|
71
|
-
}
|
|
72
|
-
.frame(width: size.sizeDp, height: size.sizeDp)
|
|
73
|
-
.background(Colors.pink09)
|
|
74
|
-
.clipShape(RoundedRectangle(cornerRadius: cornerRadius))
|
|
75
|
-
.overlay(
|
|
76
|
-
RoundedRectangle(cornerRadius: cornerRadius)
|
|
77
|
-
.stroke(theme.colors.border.default, lineWidth: 1)
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
// Badge icons — outside clip so they are not cut by border
|
|
81
|
-
if shouldShowIconTop {
|
|
82
|
-
ImageView(LOGO_MOMO_URL)
|
|
83
|
-
.frame(width: badgeSizeDp, height: badgeSizeDp)
|
|
84
|
-
.clipShape(RoundedRectangle(cornerRadius: badgeCornerRadius))
|
|
85
|
-
.offset(x: 1, y: -1)
|
|
86
|
-
.frame(
|
|
87
|
-
width: size.sizeDp,
|
|
88
|
-
height: size.sizeDp,
|
|
89
|
-
alignment: .topTrailing
|
|
90
|
-
)
|
|
91
|
-
.zIndex(1)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if shouldShowIconBottom {
|
|
95
|
-
ImageView(iconSupport)
|
|
96
|
-
.frame(width: badgeSizeDp, height: badgeSizeDp)
|
|
97
|
-
.clipShape(RoundedRectangle(cornerRadius: badgeCornerRadius))
|
|
98
|
-
.offset(x: 1, y: 1)
|
|
99
|
-
.frame(
|
|
100
|
-
width: size.sizeDp,
|
|
101
|
-
height: size.sizeDp,
|
|
102
|
-
alignment: .bottomTrailing
|
|
103
|
-
)
|
|
104
|
-
.zIndex(1)
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
.frame(width: size.sizeDp, height: size.sizeDp)
|
|
108
|
-
.accessibilityElement(children: .ignore)
|
|
109
|
-
.accessibilityLabel(automationId)
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// MARK: Internal
|
|
113
|
-
|
|
114
|
-
let size: AvatarSize
|
|
115
|
-
let rounded: Bool
|
|
116
|
-
let name: String?
|
|
117
|
-
let image: String?
|
|
118
|
-
let showIconMomo: Bool
|
|
119
|
-
let showIconSupport: Bool
|
|
120
|
-
let iconSupport: String
|
|
121
|
-
let accessibilityLabel: String?
|
|
122
|
-
|
|
123
|
-
@ViewBuilder
|
|
124
|
-
private var content: some View {
|
|
125
|
-
if let image {
|
|
126
|
-
ImageView(image, contentMode: .fill)
|
|
127
|
-
.frame(width: size.sizeDp, height: size.sizeDp)
|
|
128
|
-
.clipShape(RoundedRectangle(cornerRadius: cornerRadius))
|
|
129
|
-
} else if let name {
|
|
130
|
-
MomoText(
|
|
131
|
-
Self.avatarInitials(name),
|
|
132
|
-
typography: .descriptionXsRegular,
|
|
133
|
-
color: Colors.pink03
|
|
134
|
-
)
|
|
135
|
-
} else {
|
|
136
|
-
Icon(
|
|
137
|
-
source: "basic_person",
|
|
138
|
-
size: size.iconSizeDp,
|
|
139
|
-
color: Colors.pink03
|
|
140
|
-
)
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
private var cornerRadius: CGFloat {
|
|
145
|
-
rounded ? size.sizeDp / 2 : Spacing.XS
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
private var badgeSizeDp: CGFloat {
|
|
149
|
-
size.iconSizeDp / 2
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
private var badgeCornerRadius: CGFloat {
|
|
153
|
-
size.iconSizeDp / 4
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
private var shouldShowIconTop: Bool {
|
|
157
|
-
showIconMomo && rounded
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
private var shouldShowIconBottom: Bool {
|
|
161
|
-
showIconSupport && rounded
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
private var automationId: String {
|
|
165
|
-
accessibilityLabel ?? "Avatar\(name != nil ? "/\(name!)" : "")"
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
private static func avatarInitials(_ name: String) -> String {
|
|
169
|
-
let words = name.trimmingCharacters(in: .whitespaces)
|
|
170
|
-
.split(separator: " ")
|
|
171
|
-
.filter { !$0.isEmpty }
|
|
172
|
-
return words.suffix(2)
|
|
173
|
-
.compactMap { $0.first?.uppercased() }
|
|
174
|
-
.joined()
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// MARK: - Preview
|
|
179
|
-
#if DEBUG
|
|
180
|
-
struct Avatar_Previews: PreviewProvider {
|
|
181
|
-
static var previews: some View {
|
|
182
|
-
HStack(spacing: 12) {
|
|
183
|
-
Avatar(size: .size40)
|
|
184
|
-
Avatar(size: .size40, name: "Nguyen Van A")
|
|
185
|
-
Avatar(size: .size40, rounded: false, name: "John Doe")
|
|
186
|
-
Avatar(size: .size56, image: "https://i.pravatar.cc/150?img=1", showIconMomo: true)
|
|
187
|
-
Avatar(size: .size56, showIconSupport: true)
|
|
188
|
-
}
|
|
189
|
-
.padding()
|
|
190
|
-
.previewLayout(.sizeThatFits)
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
#endif
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
import SwiftUI
|
|
2
|
-
|
|
3
|
-
public struct BaselineView: View {
|
|
4
|
-
private let enabled: Bool
|
|
5
|
-
|
|
6
|
-
public init(enabled: Bool = true) {
|
|
7
|
-
self.enabled = enabled
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
public var body: some View {
|
|
11
|
-
if enabled {
|
|
12
|
-
GeometryReader { geometry in
|
|
13
|
-
let topInset = geometry.safeAreaInsets.top
|
|
14
|
-
let bottomInset = min(geometry.safeAreaInsets.bottom, 21)
|
|
15
|
-
|
|
16
|
-
ZStack(alignment: .topLeading) {
|
|
17
|
-
dangerZones(topInset: topInset, bottomInset: bottomInset)
|
|
18
|
-
guideLines(
|
|
19
|
-
topInset: topInset,
|
|
20
|
-
bottomInset: bottomInset,
|
|
21
|
-
width: geometry.size.width
|
|
22
|
-
)
|
|
23
|
-
}
|
|
24
|
-
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
25
|
-
.ignoresSafeArea()
|
|
26
|
-
.allowsHitTesting(false)
|
|
27
|
-
.accessibilityHidden(true)
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
private func dangerZones(topInset: CGFloat, bottomInset: CGFloat) -> some View {
|
|
33
|
-
let safeBackground = Color.red.opacity(0.15)
|
|
34
|
-
|
|
35
|
-
return ZStack(alignment: .topLeading) {
|
|
36
|
-
safeBackground
|
|
37
|
-
.frame(maxWidth: .infinity)
|
|
38
|
-
.frame(height: topInset)
|
|
39
|
-
|
|
40
|
-
HStack(spacing: 0) {
|
|
41
|
-
safeBackground
|
|
42
|
-
.frame(width: 12)
|
|
43
|
-
Spacer(minLength: 0)
|
|
44
|
-
safeBackground
|
|
45
|
-
.frame(width: 12)
|
|
46
|
-
}
|
|
47
|
-
.padding(.top, topInset)
|
|
48
|
-
.padding(.bottom, bottomInset)
|
|
49
|
-
|
|
50
|
-
VStack(spacing: 0) {
|
|
51
|
-
Spacer(minLength: 0)
|
|
52
|
-
safeBackground
|
|
53
|
-
.frame(maxWidth: .infinity)
|
|
54
|
-
.frame(height: bottomInset)
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
private func guideLines(topInset: CGFloat, bottomInset: CGFloat, width: CGFloat) -> some View {
|
|
60
|
-
ZStack(alignment: .topLeading) {
|
|
61
|
-
baselineLine(isDotted: false, color: Color(hex: 0xE400FF))
|
|
62
|
-
.offset(y: topInset)
|
|
63
|
-
|
|
64
|
-
baselineLine(color: Color(hex: 0xE400FF))
|
|
65
|
-
.offset(y: topInset + 52)
|
|
66
|
-
|
|
67
|
-
baselineLine(color: Color(hex: 0xE400FF))
|
|
68
|
-
.offset(y: topInset + 104)
|
|
69
|
-
|
|
70
|
-
bottomLine(bottomInset: bottomInset + 64, color: Color(hex: 0xE400FF))
|
|
71
|
-
|
|
72
|
-
bottomLine(bottomInset: bottomInset, isDotted: false, color: Color(hex: 0xE400FF))
|
|
73
|
-
|
|
74
|
-
baselineLine(orientation: .vertical, isDotted: false, color: Color(hex: 0xE400FF))
|
|
75
|
-
.offset(x: 12)
|
|
76
|
-
|
|
77
|
-
baselineLine(orientation: .vertical, isDotted: false, color: Color(hex: 0xE400FF))
|
|
78
|
-
.offset(x: width - 12)
|
|
79
|
-
|
|
80
|
-
baselineLine(color: Color(hex: 0xFF7A00))
|
|
81
|
-
.offset(y: topInset + 26)
|
|
82
|
-
|
|
83
|
-
bottomLine(bottomInset: bottomInset + 56, color: Color(hex: 0xFFCC00))
|
|
84
|
-
|
|
85
|
-
bottomLine(bottomInset: bottomInset + 8, color: Color(hex: 0xFFCC00))
|
|
86
|
-
|
|
87
|
-
baselineLine(orientation: .vertical, color: Color(hex: 0x00C520))
|
|
88
|
-
.frame(height: 52)
|
|
89
|
-
.offset(x: 40, y: topInset)
|
|
90
|
-
|
|
91
|
-
baselineLine(orientation: .vertical, color: Color(hex: 0x00C520))
|
|
92
|
-
.frame(height: 52)
|
|
93
|
-
.offset(x: 48, y: topInset)
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
private func bottomLine(
|
|
98
|
-
bottomInset: CGFloat,
|
|
99
|
-
isDotted: Bool = true,
|
|
100
|
-
color: Color
|
|
101
|
-
) -> some View {
|
|
102
|
-
VStack(spacing: 0) {
|
|
103
|
-
Spacer(minLength: 0)
|
|
104
|
-
baselineLine(isDotted: isDotted, color: color)
|
|
105
|
-
.padding(.bottom, bottomInset)
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
private func baselineLine(
|
|
110
|
-
orientation: BaselineOrientation = .horizontal,
|
|
111
|
-
isDotted: Bool = true,
|
|
112
|
-
color: Color
|
|
113
|
-
) -> some View {
|
|
114
|
-
BaselineLineShape(orientation: orientation)
|
|
115
|
-
.stroke(
|
|
116
|
-
color,
|
|
117
|
-
style: StrokeStyle(
|
|
118
|
-
lineWidth: 1,
|
|
119
|
-
lineCap: .round,
|
|
120
|
-
dash: isDotted ? [8, 8] : []
|
|
121
|
-
)
|
|
122
|
-
)
|
|
123
|
-
.frame(
|
|
124
|
-
maxWidth: orientation == .horizontal ? .infinity : 1,
|
|
125
|
-
maxHeight: orientation == .vertical ? .infinity : 1
|
|
126
|
-
)
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
public enum BaselineOrientation: Sendable {
|
|
131
|
-
case horizontal
|
|
132
|
-
case vertical
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
private struct BaselineLineShape: Shape {
|
|
136
|
-
let orientation: BaselineOrientation
|
|
137
|
-
|
|
138
|
-
func path(in rect: CGRect) -> Path {
|
|
139
|
-
var path = Path()
|
|
140
|
-
|
|
141
|
-
switch orientation {
|
|
142
|
-
case .horizontal:
|
|
143
|
-
path.move(to: CGPoint(x: rect.minX, y: rect.midY))
|
|
144
|
-
path.addLine(to: CGPoint(x: rect.maxX, y: rect.midY))
|
|
145
|
-
case .vertical:
|
|
146
|
-
path.move(to: CGPoint(x: rect.midX, y: rect.minY))
|
|
147
|
-
path.addLine(to: CGPoint(x: rect.midX, y: rect.maxY))
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return path
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
#if DEBUG
|
|
155
|
-
struct BaselineView_Previews: PreviewProvider {
|
|
156
|
-
static var previews: some View {
|
|
157
|
-
ZStack {
|
|
158
|
-
Colors.black01
|
|
159
|
-
BaselineView()
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
#endif
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
import Combine
|
|
2
|
-
import SwiftUI
|
|
3
|
-
|
|
4
|
-
/// Native SwiftUI port of the Compose `Carousel` component.
|
|
5
|
-
///
|
|
6
|
-
/// Mirrors the Compose API: a paging horizontal carousel driven by an
|
|
7
|
-
/// `itemCount` and a `content` builder that receives the real (modulo) index.
|
|
8
|
-
/// Supports infinite `loop`, `autoplay` with a configurable interval,
|
|
9
|
-
/// inactive-slide `opacity`/`scale` effects, optional `contentPadding`,
|
|
10
|
-
/// snap behavior, and scroll enable/disable. Snap / scroll-index callbacks
|
|
11
|
-
/// report the real item index.
|
|
12
|
-
///
|
|
13
|
-
/// Available iOS 14+ (uses a custom paging ScrollView so behavior is identical
|
|
14
|
-
/// across versions and supports the loop virtualization + transform effects
|
|
15
|
-
/// that `TabView(.page)` cannot express).
|
|
16
|
-
public struct Carousel<Content: View>: View {
|
|
17
|
-
// MARK: Lifecycle
|
|
18
|
-
|
|
19
|
-
public init(
|
|
20
|
-
itemCount: Int,
|
|
21
|
-
loop: Bool = false,
|
|
22
|
-
autoplay: Bool = false,
|
|
23
|
-
autoplayInterval: TimeInterval = 3.0,
|
|
24
|
-
enableSnap: Bool = true,
|
|
25
|
-
scrollEnabled: Bool = true,
|
|
26
|
-
inactiveSlideOpacity: Double = 1.0,
|
|
27
|
-
inactiveSlideScale: Double = 1.0,
|
|
28
|
-
contentPadding: CGFloat = 0,
|
|
29
|
-
onSnapToItem: @escaping (Int) -> Void = { _ in },
|
|
30
|
-
onScrollIndexChanged: @escaping (Int) -> Void = { _ in },
|
|
31
|
-
@ViewBuilder content: @escaping (_ index: Int) -> Content
|
|
32
|
-
) {
|
|
33
|
-
self.itemCount = itemCount
|
|
34
|
-
self.loop = loop
|
|
35
|
-
self.autoplay = autoplay
|
|
36
|
-
self.autoplayInterval = autoplayInterval
|
|
37
|
-
self.enableSnap = enableSnap
|
|
38
|
-
self.scrollEnabled = scrollEnabled
|
|
39
|
-
self.inactiveSlideOpacity = inactiveSlideOpacity
|
|
40
|
-
self.inactiveSlideScale = inactiveSlideScale
|
|
41
|
-
self.contentPadding = contentPadding
|
|
42
|
-
self.onSnapToItem = onSnapToItem
|
|
43
|
-
self.onScrollIndexChanged = onScrollIndexChanged
|
|
44
|
-
self.content = content
|
|
45
|
-
|
|
46
|
-
let virtual = loop ? itemCount * 1000 : itemCount
|
|
47
|
-
let start = loop ? (virtual / 2) - ((virtual / 2) % max(itemCount, 1)) : 0
|
|
48
|
-
self.virtualPageCount = virtual
|
|
49
|
-
_currentPage = State(initialValue: start)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// MARK: Public
|
|
53
|
-
|
|
54
|
-
public var body: some View {
|
|
55
|
-
if itemCount <= 0 {
|
|
56
|
-
EmptyView()
|
|
57
|
-
} else {
|
|
58
|
-
GeometryReader { geo in
|
|
59
|
-
let pageWidth = max(geo.size.width - contentPadding * 2, 1)
|
|
60
|
-
carouselContent(pageWidth: pageWidth, viewportWidth: geo.size.width)
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// MARK: Private
|
|
66
|
-
|
|
67
|
-
private let itemCount: Int
|
|
68
|
-
private let loop: Bool
|
|
69
|
-
private let autoplay: Bool
|
|
70
|
-
private let autoplayInterval: TimeInterval
|
|
71
|
-
private let enableSnap: Bool
|
|
72
|
-
private let scrollEnabled: Bool
|
|
73
|
-
private let inactiveSlideOpacity: Double
|
|
74
|
-
private let inactiveSlideScale: Double
|
|
75
|
-
private let contentPadding: CGFloat
|
|
76
|
-
private let onSnapToItem: (Int) -> Void
|
|
77
|
-
private let onScrollIndexChanged: (Int) -> Void
|
|
78
|
-
private let content: (Int) -> Content
|
|
79
|
-
|
|
80
|
-
private let virtualPageCount: Int
|
|
81
|
-
|
|
82
|
-
@State private var currentPage: Int
|
|
83
|
-
@State private var dragOffset: CGFloat = 0
|
|
84
|
-
@State private var isDragging: Bool = false
|
|
85
|
-
@State private var autoplayTick: Int = 0
|
|
86
|
-
|
|
87
|
-
private func realIndex(_ page: Int) -> Int {
|
|
88
|
-
((page % itemCount) + itemCount) % itemCount
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
@ViewBuilder
|
|
92
|
-
private func carouselContent(pageWidth: CGFloat, viewportWidth: CGFloat) -> some View {
|
|
93
|
-
let step = pageWidth
|
|
94
|
-
// Offset so the current page is centered within the viewport padding.
|
|
95
|
-
let baseOffset = -CGFloat(currentPage) * step + contentPadding
|
|
96
|
-
|
|
97
|
-
ZStack {
|
|
98
|
-
HStack(spacing: 0) {
|
|
99
|
-
ForEach(0 ..< virtualPageCount, id: \.self) { page in
|
|
100
|
-
slide(page: page, step: step, viewportWidth: viewportWidth)
|
|
101
|
-
.frame(width: pageWidth)
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
.offset(x: baseOffset + dragOffset)
|
|
105
|
-
.animation(isDragging ? nil : .easeInOut(duration: 0.25), value: currentPage)
|
|
106
|
-
.frame(width: viewportWidth, alignment: .leading)
|
|
107
|
-
.contentShape(Rectangle())
|
|
108
|
-
.gesture(scrollEnabled ? dragGesture(step: step) : nil)
|
|
109
|
-
}
|
|
110
|
-
.frame(width: viewportWidth)
|
|
111
|
-
.clipped()
|
|
112
|
-
.onReceive(autoplayTimer) { _ in
|
|
113
|
-
guard autoplay, itemCount > 1, !isDragging else { return }
|
|
114
|
-
advance()
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
@ViewBuilder
|
|
119
|
-
private func slide(page: Int, step: CGFloat, viewportWidth: CGFloat) -> some View {
|
|
120
|
-
// Fractional offset of this page from the current page (incl. drag).
|
|
121
|
-
let dragFraction = step > 0 ? -dragOffset / step : 0
|
|
122
|
-
let pageOffset = abs(CGFloat(currentPage - page) + dragFraction)
|
|
123
|
-
let isActive = pageOffset < 0.5
|
|
124
|
-
let scale = isActive ? 1.0 : inactiveSlideScale
|
|
125
|
-
let alpha = isActive ? 1.0 : inactiveSlideOpacity
|
|
126
|
-
|
|
127
|
-
content(realIndex(page))
|
|
128
|
-
.scaleEffect(scale)
|
|
129
|
-
.opacity(alpha)
|
|
130
|
-
.animation(.easeInOut(duration: 0.15), value: isActive)
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
private func dragGesture(step: CGFloat) -> some Gesture {
|
|
134
|
-
DragGesture(minimumDistance: 5)
|
|
135
|
-
.onChanged { value in
|
|
136
|
-
isDragging = true
|
|
137
|
-
dragOffset = value.translation.width
|
|
138
|
-
}
|
|
139
|
-
.onEnded { value in
|
|
140
|
-
isDragging = false
|
|
141
|
-
let predicted = value.predictedEndTranslation.width
|
|
142
|
-
let threshold = step / 4
|
|
143
|
-
var target = currentPage
|
|
144
|
-
if enableSnap {
|
|
145
|
-
if predicted < -threshold {
|
|
146
|
-
target = min(currentPage + 1, virtualPageCount - 1)
|
|
147
|
-
} else if predicted > threshold {
|
|
148
|
-
target = max(currentPage - 1, 0)
|
|
149
|
-
}
|
|
150
|
-
} else {
|
|
151
|
-
// Free scroll still settles to nearest page boundary.
|
|
152
|
-
let moved = step > 0 ? Int((-dragOffset / step).rounded()) : 0
|
|
153
|
-
target = min(max(currentPage + moved, 0), virtualPageCount - 1)
|
|
154
|
-
}
|
|
155
|
-
dragOffset = 0
|
|
156
|
-
setPage(target)
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
private func advance() {
|
|
161
|
-
let next = currentPage + 1
|
|
162
|
-
if next < virtualPageCount {
|
|
163
|
-
setPage(next)
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
private func setPage(_ page: Int) {
|
|
168
|
-
let clamped = min(max(page, 0), virtualPageCount - 1)
|
|
169
|
-
let changed = clamped != currentPage
|
|
170
|
-
currentPage = clamped
|
|
171
|
-
if changed {
|
|
172
|
-
let real = realIndex(clamped)
|
|
173
|
-
onSnapToItem(real)
|
|
174
|
-
onScrollIndexChanged(real)
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
private var autoplayTimer: Publishers.Autoconnect<Timer.TimerPublisher> {
|
|
179
|
-
Timer.publish(every: autoplayInterval, on: .main, in: .common).autoconnect()
|
|
180
|
-
}
|
|
181
|
-
}
|