@momo-kits/native-kits 0.163.1-sp.8-debug → 0.163.1-sp.9-debug

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.
Files changed (55) hide show
  1. package/.claude/settings.local.json +3 -1
  2. package/compose/build.gradle.kts +1 -1
  3. package/compose/compose.podspec +1 -1
  4. package/gradle.properties +1 -1
  5. package/ios/Application/Navigation/Navigator.swift +30 -24
  6. package/ios/Application/StackScreen.swift +1 -7
  7. package/ios/native-kits.podspec +1 -1
  8. package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
  9. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AvatarDemo.swift +87 -73
  10. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BadgeDemo.swift +55 -249
  11. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ButtonDemo.swift +89 -82
  12. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CarouselDemo.swift +74 -104
  13. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CheckboxDemo.swift +47 -51
  14. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ChipDemo.swift +63 -7
  15. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CollapseDemo.swift +90 -81
  16. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DateTimePickerDemo.swift +73 -97
  17. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DemoScreen.swift +418 -0
  18. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DividerDemo.swift +46 -4
  19. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +51 -47
  20. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IconButtonDemo.swift +80 -45
  21. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IconDemo.swift +67 -90
  22. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ImageDemo.swift +49 -62
  23. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InformationDemo.swift +66 -116
  24. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDemo.swift +104 -98
  25. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDropDownDemo.swift +76 -118
  26. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputMoneyDemo.swift +73 -86
  27. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputOTPDemo.swift +69 -54
  28. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputPhoneNumberDemo.swift +74 -54
  29. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputSearchDemo.swift +73 -20
  30. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputTextAreaDemo.swift +68 -38
  31. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LoaderDemo.swift +55 -52
  32. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PaginationDemo.swift +47 -51
  33. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PlaygroundData.swift +76 -0
  34. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupNotifyDemo.swift +67 -93
  35. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupPromotionDemo.swift +34 -43
  36. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ProgressInfoDemo.swift +83 -71
  37. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RadioDemo.swift +55 -23
  38. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RatingDemo.swift +58 -46
  39. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SectionDemo.swift +57 -74
  40. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SkeletonDemo.swift +41 -12
  41. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SliderDemo.swift +67 -127
  42. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SnackBarDemo.swift +54 -110
  43. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepperDemo.swift +69 -79
  44. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepsDemo.swift +78 -58
  45. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SuggestActionDemo.swift +76 -65
  46. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwipeDemo.swift +82 -127
  47. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwitchDemo.swift +44 -19
  48. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TabViewDemo.swift +92 -168
  49. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TagDemo.swift +79 -27
  50. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ThemeDemo.swift +0 -1
  51. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TitleDemo.swift +87 -92
  52. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TooltipDemo.swift +94 -163
  53. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TypographyDemo.swift +70 -70
  54. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/UploaderDemo.swift +68 -114
  55. package/package.json +2 -2
@@ -0,0 +1,418 @@
1
+ //
2
+ // DemoScreen.swift
3
+ // Example
4
+ //
5
+ // SwiftUI port of Compose's `sample/shared/.../screens/DemoScreen.kt`: the demo
6
+ // shell every component uses. `DemoScreen(preview:playground:)` renders a two-tab
7
+ // `BottomTab` — a static "Preview" showcase and an interactive, data-driven
8
+ // "Playground" props editor (BasicColumnBox / PlaygroundParams / PlaygroundView /
9
+ // PlaygroundTab). Must be pushed inside the `NavigationContainer` (BottomTab reads
10
+ // the Navigator) with `headerType: .none, scrollable: false`.
11
+ //
12
+
13
+ import MoMoUIKits
14
+ import SwiftUI
15
+
16
+ // MARK: - DemoScreen shell
17
+
18
+ @available(iOS 16.0, *)
19
+ struct DemoScreen<Preview: View, Playground: View>: View {
20
+ @ViewBuilder let preview: () -> Preview
21
+ @ViewBuilder let playground: () -> Playground
22
+
23
+ var body: some View {
24
+ BottomTab(items: [
25
+ BottomTabItem(
26
+ name: "Preview",
27
+ label: "Preview",
28
+ icon: "security_eye_open",
29
+ options: NavigationOptions(headerTitle: .default("Preview"), scrollable: true),
30
+ // StackScreen provides the scroll when scrollable == true.
31
+ screen: {
32
+ VStack(alignment: .leading, spacing: 0) { preview() }
33
+ .frame(maxWidth: .infinity, alignment: .leading)
34
+ }
35
+ ),
36
+ BottomTabItem(
37
+ name: "Playground",
38
+ label: "Playground",
39
+ icon: "24_basic_chart",
40
+ // Playground owns its own scroll (Props list), so disable the outer one.
41
+ options: NavigationOptions(headerTitle: .default("Playground"), scrollable: false),
42
+ screen: { playground() }
43
+ ),
44
+ ])
45
+ }
46
+ }
47
+
48
+ // MARK: - BasicColumnBox (titled surface card)
49
+
50
+ struct BasicColumnBox<Content: View>: View {
51
+ let title: String
52
+ let alignment: HorizontalAlignment
53
+ let background: Color?
54
+ @ViewBuilder let content: () -> Content
55
+
56
+ init(
57
+ _ title: String = "",
58
+ alignment: HorizontalAlignment = .center,
59
+ background: Color? = nil,
60
+ @ViewBuilder content: @escaping () -> Content
61
+ ) {
62
+ self.title = title
63
+ self.alignment = alignment
64
+ self.background = background
65
+ self.content = content
66
+ }
67
+
68
+ var body: some View {
69
+ VStack(alignment: .leading, spacing: Spacing.S) {
70
+ if !title.isEmpty {
71
+ MomoText(title, typography: .headerMBold)
72
+ .padding(.top, Spacing.S)
73
+ }
74
+ VStack(alignment: alignment, spacing: Spacing.S) {
75
+ content()
76
+ }
77
+ .frame(maxWidth: .infinity, alignment: alignmentFrame)
78
+ .padding(Spacing.M)
79
+ .background(
80
+ RoundedRectangle(cornerRadius: Radius.M)
81
+ .fill(background ?? defaultTheme.colors.background.surface)
82
+ )
83
+ }
84
+ .padding(Spacing.M)
85
+ }
86
+
87
+ private var alignmentFrame: Alignment {
88
+ switch alignment {
89
+ case .leading: return .leading
90
+ case .trailing: return .trailing
91
+ default: return .center
92
+ }
93
+ }
94
+ }
95
+
96
+ // MARK: - Playground model (mirrors PlaygroundParams / PlaygroundDataType)
97
+
98
+ enum PlaygroundDataType {
99
+ case string, number, option, color, radio, checkbox, slider
100
+ }
101
+
102
+ /// One selectable entry for OPTION / COLOR / RADIO params. Replaces Compose's
103
+ /// `Map<String, Any>` + `Map.Entry`. Equality is by `key`.
104
+ struct PlaygroundOption: Identifiable, Hashable {
105
+ let id = UUID()
106
+ let key: String
107
+ let value: AnyHashable
108
+
109
+ init(_ key: String, _ value: AnyHashable) {
110
+ self.key = key
111
+ self.value = value
112
+ }
113
+
114
+ static func == (lhs: PlaygroundOption, rhs: PlaygroundOption) -> Bool { lhs.key == rhs.key }
115
+ func hash(into hasher: inout Hasher) { hasher.combine(key) }
116
+ }
117
+
118
+ /// A single editable prop row. Build with the per-type factory methods so the
119
+ /// call site stays type-safe (`.radio("Type", buttonTypeList, $type)`).
120
+ struct PlaygroundParams: Identifiable {
121
+ let id = UUID()
122
+ let title: String
123
+ let type: PlaygroundDataType
124
+ let list: [PlaygroundOption]
125
+ let enable: Binding<Bool>
126
+ let required: Bool
127
+ let sliderMin: Double
128
+ let sliderMax: Double
129
+ let sliderStep: Double
130
+
131
+ // Only the binding matching `type` is non-nil.
132
+ let text: Binding<String>?
133
+ let selection: Binding<PlaygroundOption?>?
134
+ let flag: Binding<Bool>?
135
+ let number: Binding<Double>?
136
+
137
+ private init(
138
+ title: String,
139
+ type: PlaygroundDataType,
140
+ list: [PlaygroundOption] = [],
141
+ enable: Binding<Bool> = .constant(true),
142
+ required: Bool = false,
143
+ sliderMin: Double = 0,
144
+ sliderMax: Double = 100,
145
+ sliderStep: Double = 1,
146
+ text: Binding<String>? = nil,
147
+ selection: Binding<PlaygroundOption?>? = nil,
148
+ flag: Binding<Bool>? = nil,
149
+ number: Binding<Double>? = nil
150
+ ) {
151
+ self.title = title
152
+ self.type = type
153
+ self.list = list
154
+ self.enable = enable
155
+ self.required = required
156
+ self.sliderMin = sliderMin
157
+ self.sliderMax = sliderMax
158
+ self.sliderStep = sliderStep
159
+ self.text = text
160
+ self.selection = selection
161
+ self.flag = flag
162
+ self.number = number
163
+ }
164
+
165
+ static func string(_ title: String, _ text: Binding<String>, required: Bool = false, enable: Binding<Bool> = .constant(true)) -> PlaygroundParams {
166
+ PlaygroundParams(title: title, type: .string, enable: enable, required: required, text: text)
167
+ }
168
+
169
+ static func number(_ title: String, _ text: Binding<String>, required: Bool = false, enable: Binding<Bool> = .constant(true)) -> PlaygroundParams {
170
+ PlaygroundParams(title: title, type: .number, enable: enable, required: required, text: text)
171
+ }
172
+
173
+ static func option(_ title: String, _ list: [PlaygroundOption], _ selection: Binding<PlaygroundOption?>, required: Bool = false, enable: Binding<Bool> = .constant(true)) -> PlaygroundParams {
174
+ PlaygroundParams(title: title, type: .option, list: list, enable: enable, required: required, selection: selection)
175
+ }
176
+
177
+ static func color(_ title: String, _ list: [PlaygroundOption], _ selection: Binding<PlaygroundOption?>, required: Bool = false, enable: Binding<Bool> = .constant(true)) -> PlaygroundParams {
178
+ PlaygroundParams(title: title, type: .color, list: list, enable: enable, required: required, selection: selection)
179
+ }
180
+
181
+ static func radio(_ title: String, _ list: [PlaygroundOption], _ selection: Binding<PlaygroundOption?>, required: Bool = false, enable: Binding<Bool> = .constant(true)) -> PlaygroundParams {
182
+ PlaygroundParams(title: title, type: .radio, list: list, enable: enable, required: required, selection: selection)
183
+ }
184
+
185
+ static func checkbox(_ title: String, _ flag: Binding<Bool>, enable: Binding<Bool> = .constant(true)) -> PlaygroundParams {
186
+ PlaygroundParams(title: title, type: .checkbox, enable: enable, flag: flag)
187
+ }
188
+
189
+ static func slider(_ title: String, _ number: Binding<Double>, min: Double = 0, max: Double = 100, step: Double = 1, enable: Binding<Bool> = .constant(true)) -> PlaygroundParams {
190
+ PlaygroundParams(title: title, type: .slider, enable: enable, sliderMin: min, sliderMax: max, sliderStep: step, number: number)
191
+ }
192
+ }
193
+
194
+ // MARK: - PlaygroundTab (live component + scrollable props list)
195
+
196
+ @available(iOS 16.0, *)
197
+ struct PlaygroundTab<Component: View>: View {
198
+ @ViewBuilder let component: () -> Component
199
+ let params: [PlaygroundParams]
200
+ var isHideComponent: Bool = false
201
+
202
+ var body: some View {
203
+ VStack(spacing: 0) {
204
+ if !isHideComponent {
205
+ BasicColumnBox("Component", alignment: .leading) { component() }
206
+ }
207
+ ScrollView {
208
+ BasicColumnBox("Props", alignment: .leading) {
209
+ ForEach(params) { param in
210
+ MomoText(param.title, typography: .headerDefaultBold)
211
+ .opacity(param.enable.wrappedValue ? 1 : 0.4)
212
+ PlaygroundView(param: param)
213
+ Spacer().frame(height: Spacing.L)
214
+ }
215
+ }
216
+ }
217
+ }
218
+ }
219
+ }
220
+
221
+ // MARK: - PlaygroundView (per-type editor, mirrors PlayGroundView.kt)
222
+
223
+ @available(iOS 16.0, *)
224
+ struct PlaygroundView: View {
225
+ let param: PlaygroundParams
226
+ @EnvironmentObject private var navigator: Navigator
227
+ @State private var isUndefined = false
228
+ @State private var initialSelection: PlaygroundOption?
229
+ @State private var didCapture = false
230
+
231
+ var body: some View {
232
+ editor
233
+ .opacity(param.enable.wrappedValue ? 1 : 0.4)
234
+ .disabled(!param.enable.wrappedValue)
235
+ .allowsHitTesting(param.enable.wrappedValue)
236
+ .onAppear {
237
+ if !didCapture {
238
+ initialSelection = param.selection?.wrappedValue
239
+ didCapture = true
240
+ }
241
+ }
242
+ }
243
+
244
+ @ViewBuilder
245
+ private var editor: some View {
246
+ switch param.type {
247
+ case .string, .number: stringEditor
248
+ case .option, .color: optionEditor
249
+ case .radio: radioEditor
250
+ case .checkbox: checkboxEditor
251
+ case .slider: sliderEditor
252
+ }
253
+ }
254
+
255
+ // STRING / NUMBER
256
+ @ViewBuilder
257
+ private var stringEditor: some View {
258
+ if let text = param.text {
259
+ Input(
260
+ text: text,
261
+ keyboardType: param.type == .number ? .numberPad : .default,
262
+ onChangeText: { _ in isUndefined = false }
263
+ )
264
+ if !param.required {
265
+ undefinedCheckbox { if isUndefined { text.wrappedValue = "" } }
266
+ }
267
+ }
268
+ }
269
+
270
+ // OPTION / COLOR
271
+ @ViewBuilder
272
+ private var optionEditor: some View {
273
+ if let selection = param.selection {
274
+ HStack {
275
+ Spacer().frame(width: 30)
276
+ MomoText(selection.wrappedValue?.key ?? "", typography: .bodyDefaultRegular)
277
+ Spacer()
278
+ Icon(source: "arrow_chevron_down", size: 20)
279
+ .padding(.trailing, Spacing.XS)
280
+ }
281
+ .frame(height: 40)
282
+ .overlay(
283
+ RoundedRectangle(cornerRadius: Radius.XS)
284
+ .stroke(defaultTheme.colors.border.default, lineWidth: 1)
285
+ )
286
+ .contentShape(Rectangle())
287
+ .onTapGesture { openPicker(selection: selection) }
288
+
289
+ if !param.required {
290
+ undefinedCheckbox { if isUndefined { selection.wrappedValue = initialSelection } }
291
+ }
292
+ }
293
+ }
294
+
295
+ // RADIO
296
+ @ViewBuilder
297
+ private var radioEditor: some View {
298
+ if let selection = param.selection {
299
+ ForEach(param.list) { option in
300
+ Radio(
301
+ value: option.key,
302
+ groupValue: Binding(
303
+ get: { AnyHashable(selection.wrappedValue?.key ?? "") },
304
+ set: { _ in }
305
+ ),
306
+ onChange: { isUndefined = false; selection.wrappedValue = option },
307
+ label: option.key
308
+ )
309
+ }
310
+ if !param.required {
311
+ undefinedCheckbox { if isUndefined { selection.wrappedValue = initialSelection } }
312
+ }
313
+ }
314
+ }
315
+
316
+ // CHECKBOX
317
+ @ViewBuilder
318
+ private var checkboxEditor: some View {
319
+ if let flag = param.flag {
320
+ Checkbox(flag, title: flag.wrappedValue.description)
321
+ }
322
+ }
323
+
324
+ // SLIDER
325
+ @ViewBuilder
326
+ private var sliderEditor: some View {
327
+ if let number = param.number {
328
+ MomoText(formatSlider(number.wrappedValue), typography: .headerDefaultBold)
329
+ MomoSlider(
330
+ min: param.sliderMin,
331
+ max: param.sliderMax,
332
+ step: param.sliderStep,
333
+ low: number.wrappedValue,
334
+ disableRange: true,
335
+ onValueChanged: { low, _, _ in number.wrappedValue = low }
336
+ )
337
+ .frame(maxWidth: .infinity)
338
+ }
339
+ }
340
+
341
+ // "is undefined" toggle for optional params.
342
+ @ViewBuilder
343
+ private func undefinedCheckbox(_ onToggle: @escaping () -> Void) -> some View {
344
+ Checkbox(
345
+ Binding(get: { isUndefined }, set: { _ in }),
346
+ onChange: { _ in isUndefined.toggle(); onToggle() },
347
+ title: "is undefined"
348
+ )
349
+ }
350
+
351
+ private func openPicker(selection: Binding<PlaygroundOption?>) {
352
+ let isColor = param.type == .color
353
+ navigator.showBottomSheet({
354
+ PlaygroundOptionPicker(list: param.list, isColor: isColor) { picked in
355
+ isUndefined = false
356
+ selection.wrappedValue = picked
357
+ navigator.hideOverplay()
358
+ }
359
+ }, header: .title("Choose option"))
360
+ }
361
+
362
+ private func formatSlider(_ v: Double) -> String {
363
+ v == v.rounded() ? String(Int(v)) : String(format: "%.1f", v)
364
+ }
365
+ }
366
+
367
+ // MARK: - Option picker (searchable bottom-sheet list)
368
+
369
+ @available(iOS 16.0, *)
370
+ private struct PlaygroundOptionPicker: View {
371
+ let list: [PlaygroundOption]
372
+ let isColor: Bool
373
+ let onSelect: (PlaygroundOption) -> Void
374
+
375
+ @State private var query = ""
376
+
377
+ private var filtered: [PlaygroundOption] {
378
+ query.isEmpty ? list : list.filter { $0.key.localizedCaseInsensitiveContains(query) }
379
+ }
380
+
381
+ var body: some View {
382
+ VStack(spacing: 0) {
383
+ InputSearch(props: InputSearchProps(
384
+ text: $query,
385
+ placeholder: "Search input",
386
+ onChangeText: { query = $0.replacingOccurrences(of: "\n", with: " ") }
387
+ ))
388
+ .padding(.horizontal, Spacing.L)
389
+
390
+ ScrollView {
391
+ LazyVStack(alignment: .leading, spacing: 0) {
392
+ ForEach(filtered) { item in
393
+ HStack(spacing: Spacing.S) {
394
+ if isColor, let color = item.value.base as? Color {
395
+ RoundedRectangle(cornerRadius: Radius.S)
396
+ .fill(color)
397
+ .frame(width: 36, height: 36)
398
+ .overlay(
399
+ RoundedRectangle(cornerRadius: Radius.S)
400
+ .stroke(defaultTheme.colors.border.default, lineWidth: 1)
401
+ )
402
+ } else if let iconName = item.value.base as? String {
403
+ Icon(source: iconName, size: 24)
404
+ }
405
+ MomoText(item.key, typography: .bodyDefaultRegular)
406
+ }
407
+ .frame(maxWidth: .infinity, alignment: .leading)
408
+ .padding(.vertical, Spacing.XS)
409
+ .contentShape(Rectangle())
410
+ .onTapGesture { onSelect(item) }
411
+ }
412
+ }
413
+ .padding(Spacing.L)
414
+ }
415
+ }
416
+ .frame(height: 600)
417
+ }
418
+ }
@@ -1,14 +1,56 @@
1
- import SwiftUI
1
+ //
2
+ // DividerDemo.swift
3
+ // Example
4
+ //
5
+ // Preview + Playground demo for MomoDivider.
6
+ //
7
+
2
8
  import MoMoUIKits
9
+ import SwiftUI
3
10
 
4
11
  struct DividerDemo: View {
5
12
  var body: some View {
6
- VStack(alignment: .leading, spacing: 8) {
13
+ if #available(iOS 16.0, *) {
14
+ DemoScreen(preview: { DividerPreview() }, playground: { DividerPlayground() })
15
+ } else {
16
+ Text("Requires iOS 16+").padding()
17
+ }
18
+ }
19
+ }
20
+
21
+ // MARK: - Preview
22
+
23
+ private struct DividerPreview: View {
24
+ var body: some View {
25
+ BasicColumnBox("Between items", alignment: .leading) {
7
26
  ForEach(0..<5, id: \.self) { index in
8
- Text("Item\(index)")
27
+ MomoText("Item \(index)", typography: .bodyDefaultRegular)
9
28
  MomoDivider()
10
29
  }
11
30
  }
12
- .padding()
31
+ }
32
+ }
33
+
34
+ // MARK: - Playground
35
+
36
+ // MomoDivider takes no props; the playground varies how many are rendered.
37
+ @available(iOS 16.0, *)
38
+ private struct DividerPlayground: View {
39
+ @State private var count: Double = 3
40
+
41
+ var body: some View {
42
+ PlaygroundTab(
43
+ component: {
44
+ VStack(alignment: .leading, spacing: Spacing.S) {
45
+ ForEach(0..<Int(count), id: \.self) { index in
46
+ MomoText("Item \(index)", typography: .bodyDefaultRegular)
47
+ MomoDivider()
48
+ }
49
+ }
50
+ },
51
+ params: [
52
+ .slider("Count", $count, min: 1, max: 6, step: 1),
53
+ ]
54
+ )
13
55
  }
14
56
  }
@@ -13,11 +13,16 @@ public struct ScreenUsage: Hashable, Identifiable {
13
13
  public let title: String
14
14
  public let destination: AnyView
15
15
  public let icon: String
16
-
17
- public init(title: String, destination: AnyView, icon: String) {
16
+ /// True when the destination hosts its own chrome (e.g. a `BottomTab` — the
17
+ /// Preview/Playground `DemoScreen` or `BottomTabDemo`); such screens are pushed
18
+ /// with `headerType: .none, scrollable: false`.
19
+ public let hostsOwnChrome: Bool
20
+
21
+ public init(title: String, destination: AnyView, icon: String, hostsOwnChrome: Bool = false) {
18
22
  self.title = title
19
23
  self.destination = destination
20
24
  self.icon = icon
25
+ self.hostsOwnChrome = hostsOwnChrome
21
26
  }
22
27
 
23
28
  public static func == (lhs: ScreenUsage, rhs: ScreenUsage) -> Bool {
@@ -34,52 +39,51 @@ public func getIcon(icon: String) -> String {
34
39
  }
35
40
 
36
41
  public let ScreenUsages: [ScreenUsage] = [
37
- ScreenUsage(title: "Badge", destination: AnyView(BadgeDemo()), icon: getIcon(icon: "Badge")),
38
- ScreenUsage(title: "Button", destination: AnyView(ButtonDemo()), icon: getIcon(icon: "Button")),
39
- ScreenUsage(title: "CheckBox", destination: AnyView(CheckboxDemo()), icon: getIcon(icon: "Checkbox")),
40
- ScreenUsage(title: "Chip", destination: AnyView(ChipDemo()), icon: getIcon(icon: "Chip")),
41
- ScreenUsage(title: "Icon", destination: AnyView(IconDemo()), icon: getIcon(icon: "Icon")),
42
- ScreenUsage(title: "Image", destination: AnyView(ImageDemo()), icon: getIcon(icon: "Layout")),
43
- ScreenUsage(title: "Baseline View", destination: AnyView(BaselineViewDemo()), icon: getIcon(icon: "Layout")),
44
- ScreenUsage(title: "Section", destination: AnyView(SectionDemo()), icon: getIcon(icon: "Layout")),
45
- ScreenUsage(title: "Input", destination: AnyView(InputDemo()), icon: getIcon(icon: "FormInput")),
46
- ScreenUsage(title: "Input Phone Number", destination: AnyView(InputPhoneNumberDemo()), icon: getIcon(icon: "FormInput")),
47
- ScreenUsage(title: "InputTextArea", destination: AnyView(InputTextAreaDemo()), icon: getIcon(icon: "FormInput")),
48
- ScreenUsage(title: "InputSearch", destination: AnyView(InputSearchDemo()), icon: getIcon(icon: "FormInput")),
49
- ScreenUsage(title: "PopupNotify", destination: AnyView(PopupNotifyDemo()), icon: getIcon(icon: "Popup")),
50
- ScreenUsage(title: "PopupPromotion", destination: AnyView(PopupPromotionDemo()), icon: getIcon(icon: "Popup")),
51
- ScreenUsage(title: "SnackBar", destination: AnyView(SnackBarDemo()), icon: getIcon(icon: "Popup")),
52
- ScreenUsage(title: "Switch", destination: AnyView(SwitchDemo()), icon: getIcon(icon: "Toggle")),
53
- ScreenUsage(title: "Text", destination: AnyView(TypographyDemo()), icon: getIcon(icon: "Typography")),
42
+ ScreenUsage(title: "Badge", destination: AnyView(BadgeDemo()), icon: getIcon(icon: "Badge"), hostsOwnChrome: true),
43
+ ScreenUsage(title: "Button", destination: AnyView(ButtonDemo()), icon: getIcon(icon: "Button"), hostsOwnChrome: true),
44
+ ScreenUsage(title: "CheckBox", destination: AnyView(CheckboxDemo()), icon: getIcon(icon: "Checkbox"), hostsOwnChrome: true),
45
+ ScreenUsage(title: "Chip", destination: AnyView(ChipDemo()), icon: getIcon(icon: "Chip"), hostsOwnChrome: true),
46
+ ScreenUsage(title: "Icon", destination: AnyView(IconDemo()), icon: getIcon(icon: "Icon"), hostsOwnChrome: true),
47
+ ScreenUsage(title: "Image", destination: AnyView(ImageDemo()), icon: getIcon(icon: "Layout"), hostsOwnChrome: true),
48
+ ScreenUsage(title: "Section", destination: AnyView(SectionDemo()), icon: getIcon(icon: "Layout"), hostsOwnChrome: true),
49
+ ScreenUsage(title: "Input", destination: AnyView(InputDemo()), icon: getIcon(icon: "FormInput"), hostsOwnChrome: true),
50
+ ScreenUsage(title: "Input Phone Number", destination: AnyView(InputPhoneNumberDemo()), icon: getIcon(icon: "FormInput"), hostsOwnChrome: true),
51
+ ScreenUsage(title: "InputTextArea", destination: AnyView(InputTextAreaDemo()), icon: getIcon(icon: "FormInput"), hostsOwnChrome: true),
52
+ ScreenUsage(title: "InputSearch", destination: AnyView(InputSearchDemo()), icon: getIcon(icon: "FormInput"), hostsOwnChrome: true),
53
+ ScreenUsage(title: "PopupNotify", destination: AnyView(PopupNotifyDemo()), icon: getIcon(icon: "Popup"), hostsOwnChrome: true),
54
+ ScreenUsage(title: "PopupPromotion", destination: AnyView(PopupPromotionDemo()), icon: getIcon(icon: "Popup"), hostsOwnChrome: true),
55
+ ScreenUsage(title: "SnackBar", destination: AnyView(SnackBarDemo()), icon: getIcon(icon: "Popup"), hostsOwnChrome: true),
56
+ ScreenUsage(title: "Switch", destination: AnyView(SwitchDemo()), icon: getIcon(icon: "Toggle"), hostsOwnChrome: true),
57
+ ScreenUsage(title: "Text", destination: AnyView(TypographyDemo()), icon: getIcon(icon: "Typography"), hostsOwnChrome: true),
54
58
  // ScreenUsage(title: "TrustBanner", destination: AnyView(TrustBanner(onPress: {_ in })), icon: getIcon(icon: "Typography")),
55
- ScreenUsage(title: "Information", destination: AnyView(InformationDemo()), icon: getIcon(icon: "Typography")),
56
- ScreenUsage(title: "Avatar", destination: AnyView(AvatarDemo()), icon: getIcon(icon: "Avatar")),
57
- ScreenUsage(title: "Carousel", destination: AnyView(CarouselDemo()), icon: getIcon(icon: "Carousel")),
58
- ScreenUsage(title: "Divider", destination: AnyView(DividerDemo()), icon: getIcon(icon: "Divider")),
59
- ScreenUsage(title: "IconButton", destination: AnyView(IconButtonDemo()), icon: getIcon(icon: "Button")),
60
- ScreenUsage(title: "Loader", destination: AnyView(LoaderDemo()), icon: getIcon(icon: "Loader")),
61
- ScreenUsage(title: "Pagination", destination: AnyView(PaginationDemo()), icon: getIcon(icon: "Badge")),
62
- ScreenUsage(title: "Radio", destination: AnyView(RadioDemo()), icon: getIcon(icon: "Radio")),
63
- ScreenUsage(title: "Rating", destination: AnyView(RatingDemo()), icon: getIcon(icon: "Rating")),
64
- ScreenUsage(title: "Skeleton", destination: AnyView(SkeletonDemo()), icon: getIcon(icon: "Skeleton")),
65
- ScreenUsage(title: "SuggestAction", destination: AnyView(SuggestActionDemo()), icon: getIcon(icon: "Information")),
66
- ScreenUsage(title: "Tag", destination: AnyView(TagDemo()), icon: getIcon(icon: "Tag")),
67
- ScreenUsage(title: "Title", destination: AnyView(TitleDemo()), icon: getIcon(icon: "Typography")),
68
- ScreenUsage(title: "Collapse", destination: AnyView(CollapseDemo()), icon: getIcon(icon: "Layout")),
69
- ScreenUsage(title: "Stepper", destination: AnyView(StepperDemo()), icon: getIcon(icon: "Layout")),
70
- ScreenUsage(title: "Uploader", destination: AnyView(UploaderDemo()), icon: getIcon(icon: "FormInput")),
71
- ScreenUsage(title: "Swipe", destination: AnyView(SwipeDemo()), icon: getIcon(icon: "Layout")),
72
- ScreenUsage(title: "InputDropDown", destination: AnyView(InputDropDownDemo()), icon: getIcon(icon: "FormInput")),
73
- ScreenUsage(title: "InputMoney", destination: AnyView(InputMoneyDemo()), icon: getIcon(icon: "FormInput")),
74
- ScreenUsage(title: "InputOTP", destination: AnyView(InputOTPDemo()), icon: getIcon(icon: "FormInput")),
75
- ScreenUsage(title: "ProgressInfo", destination: AnyView(ProgressInfoDemo()), icon: getIcon(icon: "Layout")),
76
- ScreenUsage(title: "Steps", destination: AnyView(StepsDemo()), icon: getIcon(icon: "Layout")),
77
- ScreenUsage(title: "Slider", destination: AnyView(SliderDemo()), icon: getIcon(icon: "Layout")),
78
- ScreenUsage(title: "TabView", destination: AnyView(TabViewDemo()), icon: getIcon(icon: "Layout")),
79
- ScreenUsage(title: "Tooltip", destination: AnyView(TooltipDemo()), icon: getIcon(icon: "Layout")),
80
- ScreenUsage(title: "DateTimePicker", destination: AnyView(DateTimePickerDemo()), icon: getIcon(icon: "Layout")),
59
+ ScreenUsage(title: "Information", destination: AnyView(InformationDemo()), icon: getIcon(icon: "Typography"), hostsOwnChrome: true),
60
+ ScreenUsage(title: "Avatar", destination: AnyView(AvatarDemo()), icon: getIcon(icon: "Avatar"), hostsOwnChrome: true),
61
+ ScreenUsage(title: "Carousel", destination: AnyView(CarouselDemo()), icon: getIcon(icon: "Carousel"), hostsOwnChrome: true),
62
+ ScreenUsage(title: "Divider", destination: AnyView(DividerDemo()), icon: getIcon(icon: "Divider"), hostsOwnChrome: true),
63
+ ScreenUsage(title: "IconButton", destination: AnyView(IconButtonDemo()), icon: getIcon(icon: "Button"), hostsOwnChrome: true),
64
+ ScreenUsage(title: "Loader", destination: AnyView(LoaderDemo()), icon: getIcon(icon: "Loader"), hostsOwnChrome: true),
65
+ ScreenUsage(title: "Pagination", destination: AnyView(PaginationDemo()), icon: getIcon(icon: "Badge"), hostsOwnChrome: true),
66
+ ScreenUsage(title: "Radio", destination: AnyView(RadioDemo()), icon: getIcon(icon: "Radio"), hostsOwnChrome: true),
67
+ ScreenUsage(title: "Rating", destination: AnyView(RatingDemo()), icon: getIcon(icon: "Rating"), hostsOwnChrome: true),
68
+ ScreenUsage(title: "Skeleton", destination: AnyView(SkeletonDemo()), icon: getIcon(icon: "Skeleton"), hostsOwnChrome: true),
69
+ ScreenUsage(title: "SuggestAction", destination: AnyView(SuggestActionDemo()), icon: getIcon(icon: "Information"), hostsOwnChrome: true),
70
+ ScreenUsage(title: "Tag", destination: AnyView(TagDemo()), icon: getIcon(icon: "Tag"), hostsOwnChrome: true),
71
+ ScreenUsage(title: "Title", destination: AnyView(TitleDemo()), icon: getIcon(icon: "Typography"), hostsOwnChrome: true),
72
+ ScreenUsage(title: "Collapse", destination: AnyView(CollapseDemo()), icon: getIcon(icon: "Layout"), hostsOwnChrome: true),
73
+ ScreenUsage(title: "Stepper", destination: AnyView(StepperDemo()), icon: getIcon(icon: "Layout"), hostsOwnChrome: true),
74
+ ScreenUsage(title: "Uploader", destination: AnyView(UploaderDemo()), icon: getIcon(icon: "FormInput"), hostsOwnChrome: true),
75
+ ScreenUsage(title: "Swipe", destination: AnyView(SwipeDemo()), icon: getIcon(icon: "Layout"), hostsOwnChrome: true),
76
+ ScreenUsage(title: "InputDropDown", destination: AnyView(InputDropDownDemo()), icon: getIcon(icon: "FormInput"), hostsOwnChrome: true),
77
+ ScreenUsage(title: "InputMoney", destination: AnyView(InputMoneyDemo()), icon: getIcon(icon: "FormInput"), hostsOwnChrome: true),
78
+ ScreenUsage(title: "InputOTP", destination: AnyView(InputOTPDemo()), icon: getIcon(icon: "FormInput"), hostsOwnChrome: true),
79
+ ScreenUsage(title: "ProgressInfo", destination: AnyView(ProgressInfoDemo()), icon: getIcon(icon: "Layout"), hostsOwnChrome: true),
80
+ ScreenUsage(title: "Steps", destination: AnyView(StepsDemo()), icon: getIcon(icon: "Layout"), hostsOwnChrome: true),
81
+ ScreenUsage(title: "Slider", destination: AnyView(SliderDemo()), icon: getIcon(icon: "Layout"), hostsOwnChrome: true),
82
+ ScreenUsage(title: "TabView", destination: AnyView(TabViewDemo()), icon: getIcon(icon: "Layout"), hostsOwnChrome: true),
83
+ ScreenUsage(title: "Tooltip", destination: AnyView(TooltipDemo()), icon: getIcon(icon: "Layout"), hostsOwnChrome: true),
84
+ ScreenUsage(title: "DateTimePicker", destination: AnyView(DateTimePickerDemo()), icon: getIcon(icon: "Layout"), hostsOwnChrome: true),
81
85
  ScreenUsage(title: "Navigation", destination: AnyView(NavigationDemo()), icon: getIcon(icon: "Layout")),
82
- ScreenUsage(title: "Bottom Tab", destination: AnyView(BottomTabDemo()), icon: getIcon(icon: "Layout")),
86
+ ScreenUsage(title: "Bottom Tab", destination: AnyView(BottomTabDemo()), icon: getIcon(icon: "Layout"), hostsOwnChrome: true),
83
87
  ScreenUsage(title: "Animated Header", destination: AnyView(AnimatedHeaderDemo()), icon: getIcon(icon: "Layout")),
84
88
  ScreenUsage(title: "Theme", destination: AnyView(ThemeDemo()), icon: getIcon(icon: "Typography")),
85
89
  ScreenUsage(title: "Localize", destination: AnyView(LocalizeDemo()), icon: getIcon(icon: "Typography")),
@@ -233,7 +237,7 @@ public struct HomeItemView: View {
233
237
  public var body: some View {
234
238
  ForEach(screenUsages) { screenUsage in
235
239
  SwiftUI.Button {
236
- let options = screenUsage.title == "Bottom Tab"
240
+ let options = screenUsage.hostsOwnChrome
237
241
  ? NavigationOptions(
238
242
  title: screenUsage.title,
239
243
  headerType: .none,