Skip to content

feat: add core components - #4

Merged
romchornyi merged 2 commits into
masterfrom
import/core-components
Jul 14, 2026
Merged

feat: add core components#4
romchornyi merged 2 commits into
masterfrom
import/core-components

Conversation

@romchornyi

@romchornyi romchornyi commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Fourth migration PR — core presentational components on top of Foundation + primitives.

Components (11)

SearchBar, Toast, BottomSheet, NavigationBar, MenuItem, RadioButtonRow, DashSwitch, SystemMessageView, AddressFieldView, DashAmount, Table List/List1View

Assets added

menu-receive.disabled, menu-send.disabled (MenuItem), info-rect (SystemMessage), toast-no-wifi (Toast)

iOS 14 compat fixes applied during migration ⚠️

The source versions had latent issues that either broke the build or the declared availability:

  • SystemMessageView: added missing @available(iOS 14, macOS 11, *) (was absent → build error).
  • SystemMessageView / AddressFieldView / SearchBar / Toast: replaced iOS 16+ .rect(cornerRadius:) / .contentShape(.rect) with RoundedRectangle(cornerRadius:style:.continuous) / Rectangle() to honor the declared iOS 14/15 support.

Notes

  • swift build passes.
  • Not yet migrated: CoinSelector, EnterAmount + NumericKeyboard, Converter, TopIntro.

Summary by CodeRabbit

  • New Features
    • Added a reusable address input with label/placeholder, validation messaging, focus styling, QR scan + clear, optional paste, and multiline support on supported OS versions.
    • Added a BottomSheet component with filled or self-sizing behavior, plus navigation-style header controls.
    • Added reusable UI elements: formatted Dash amount display (sign/unavailable handling), switches, menu items, navigation bars, radio/checkbox rows, search bars (clear/cancel), system messages (optional actions/close), toasts, and a label-and-value row.
  • Style
    • Added new media assets for disabled menu actions, system message iconography, and offline/toast visuals.

Add the core presentational components on top of Foundation + primitives:
SearchBar, Toast, BottomSheet, NavigationBar, MenuItem, RadioButtonRow,
DashSwitch, SystemMessageView, AddressFieldView, DashAmount, List1View, plus
the assets they use (menu-*.disabled, info-rect, toast-no-wifi).

iOS 14 compatibility fixes applied during migration:
- SystemMessageView: add missing @available(iOS 14, macOS 11, *) (was absent,
  broke the build)
- SystemMessageView / AddressFieldView / SearchBar / Toast: replace iOS 16+
  .rect(cornerRadius:) and .contentShape(.rect) with RoundedRectangle(...,
  style: .continuous) / Rectangle() so the declared iOS 14/15 surface holds.
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1583cdef-1cd0-4337-a3c3-c2b4722945e6

📥 Commits

Reviewing files that changed from the base of the PR and between 700b9ce and f0668df.

📒 Files selected for processing (4)
  • Sources/DashUIKit/Components/MenuItem.swift
  • Sources/DashUIKit/Components/NavigationBar.swift
  • Sources/DashUIKit/Components/RadioButtonRow.swift
  • Sources/DashUIKit/Components/SystemMessageView.swift
🚧 Files skipped from review as they are similar to previous changes (4)
  • Sources/DashUIKit/Components/RadioButtonRow.swift
  • Sources/DashUIKit/Components/SystemMessageView.swift
  • Sources/DashUIKit/Components/NavigationBar.swift
  • Sources/DashUIKit/Components/MenuItem.swift

📝 Walkthrough

Walkthrough

Adds 13 public SwiftUI components for address/search input, navigation, sheets, menus, amounts, switches, messages, toasts, selection rows, and labeled values, plus four image-set asset definitions and debug previews.

Changes

DashUIKit component suite

Layer / File(s) Summary
Display and menu primitives
Sources/DashUIKit/Components/DashAmount.swift, Sources/DashUIKit/Components/MenuItem.swift, Sources/DashUIKit/Components/DashSwitch.swift, Sources/DashUIKit/Resources/Media.xcassets/Icons & Illustrations/Menu/*
Adds localized Dash amount formatting, configurable menu accessories, platform-specific switch styling, and disabled menu icon assets.
Navigation and sheet presentation
Sources/DashUIKit/Components/NavigationBar.swift, Sources/DashUIKit/Components/BottomSheet.swift
Adds composable navigation bars, navigation elements, greedy and self-sizing bottom sheets, measured detents, and presentation styling.
Address and search inputs
Sources/DashUIKit/Components/AddressFieldView.swift, Sources/DashUIKit/Components/SearchBar.swift
Adds focus-aware address entry with scan, clear, paste, error, disabled, and multiline states, plus version-specific search and cancel behavior.
System messages and toasts
Sources/DashUIKit/Components/SystemMessageView.swift, Sources/DashUIKit/Components/Toast.swift, Sources/DashUIKit/Resources/Media.xcassets/Icons & Illustrations/System message/info-rect.imageset/Contents.json, Sources/DashUIKit/Resources/Media.xcassets/Icons & Illustrations/Toast/toast-no-wifi.imageset/Contents.json
Adds configurable system messages and styled toast variants with optional actions, dismissal, loading presentation, blur, and supporting assets.
Selection and list rows
Sources/DashUIKit/Components/RadioButtonRow.swift, Sources/DashUIKit/Table List/List1View.swift
Adds radio or checkbox rows with optional metadata and icons, and a labeled value row for iOS and macOS.

Estimated code review effort: 5 (Critical) | ~90 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding core UI components.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch import/core-components

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@romchornyi

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (4)
Sources/DashUIKit/Table List/List1View.swift (1)

20-29: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use let instead of var for view properties.

SwiftUI view properties should be immutable. Declaring label and value as public var allows external mutation, which is non-idiomatic for a value-type View. Additionally, the default placeholder values "Label" and "Value" could accidentally surface in production if a caller omits them.

♻️ Proposed refactor
-    public var label: String
-    public var value: String
+    public let label: String
+    public let value: String
 
-    public init(label: String = "Label", value: String = "Value") {
+    public init(label: String, value: String) {
         self.label = label
         self.value = value
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/DashUIKit/Table` List/List1View.swift around lines 20 - 29, In
List1View, make the label and value stored properties immutable by changing both
public var declarations to public let, and remove the placeholder default
arguments from its initializer so callers must provide explicit values.
Sources/DashUIKit/Components/SearchBar.swift (1)

48-214: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

magnifyingGlass and clearButton are duplicated between SearchBarFocused and SearchBarLegacy.

Both private structs define identical magnifyingGlass and clearButton computed properties. Extracting these into a shared private helper or a shared protocol would reduce maintenance burden if the icons or styling ever change.

♻️ Suggested approach: extract shared views
+// Shared between both variants
+private struct SearchBarMagnifyingGlass: View {
+    var body: some View {
+        Image(dash: .custom("searchbar-magnifyingglass-icon", bundle: .dashUIKit))
+            .resizable()
+            .scaledToFit()
+            .frame(maxHeight: 15)
+    }
+}
+
+private struct SearchBarClearButton: View {
+    `@Binding` var text: String
+    var body: some View {
+        if !text.isEmpty {
+            Button(action: { text = "" }) {
+                Image(dash: .custom("searchbar-xmark-icon", bundle: .dashUIKit))
+                    .resizable()
+                    .scaledToFit()
+                    .frame(maxHeight: 15)
+                    .frame(width: 44, height: 44)
+                    .contentShape(Rectangle())
+            }
+            .accessibilityLabel(Text(NSLocalizedString("Clear", bundle: .module, comment: "DashUIKit")))
+        }
+    }
+}

Then replace the inline computed properties in both SearchBarFocused and SearchBarLegacy with SearchBarMagnifyingGlass() and SearchBarClearButton(text: $text).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/DashUIKit/Components/SearchBar.swift` around lines 48 - 214, Extract
the duplicated magnifyingGlass and clearButton implementations from
SearchBarFocused and SearchBarLegacy into shared private views, such as
SearchBarMagnifyingGlass and SearchBarClearButton(text:), preserving the
existing icons, sizing, accessibility label, and binding behavior; replace both
structs’ computed properties with those shared components.
Sources/DashUIKit/Components/DashAmount.swift (1)

37-45: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Static NumberFormatter captures .current locale at first access.

The formatter is initialized once as a static property, so f.locale = .current is locked to whatever locale was active when DashAmountFormat was first referenced. If the user changes their locale/language while the app is running, amounts will continue to display in the stale locale until the process restarts.

Consider using Formatter.withLocale(.current) per call (iOS 15+) or recreating the formatter when Locale.current changes:

♻️ Suggested approach: recompute locale per call
 private enum DashAmountFormat {
     static let duffsPerDash: Decimal = 100_000_000

-    static let numberFormatter: NumberFormatter = {
-        let f = NumberFormatter()
-        f.numberStyle = .decimal
-        f.locale = .current
-        f.minimumFractionDigits = 0
-        f.maximumFractionDigits = 5
-        f.usesGroupingSeparator = true
-        return f
-    }()
+    static func makeFormatter() -> NumberFormatter {
+        let f = NumberFormatter()
+        f.numberStyle = .decimal
+        f.locale = .current
+        f.minimumFractionDigits = 0
+        f.maximumFractionDigits = 5
+        f.usesGroupingSeparator = true
+        return f
+    }

     static func string(forDuffs duffs: Int64) -> String {
         let value = Decimal(duffs) / duffsPerDash
-        return numberFormatter.string(from: value as NSNumber) ?? "\(value)"
+        return makeFormatter().string(from: value as NSNumber) ?? "\(value)"
     }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/DashUIKit/Components/DashAmount.swift` around lines 37 - 45,
DashAmountFormat’s static numberFormatter captures Locale.current only on first
access, causing stale formatting after runtime locale changes. Update the
formatting logic to obtain or configure a NumberFormatter with Locale.current
for each formatting call, using Formatter.withLocale(.current) where supported,
and remove reliance on the permanently cached locale in numberFormatter.
Sources/DashUIKit/Components/Toast.swift (1)

24-31: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

BackgroundBlurView has no macOS fallback, yet Toast declares macOS 11 availability.

The entire file is guarded by #if canImport(UIKit), so on macOS neither BackgroundBlurView nor Toast will compile. The @available(iOS 14, macOS 11, *) annotations on ToastStyle and Toast are misleading — API consumers may expect macOS support that doesn't exist. Either remove the macOS availability target or provide a SwiftUI-native blur fallback (e.g., .material(.ultraThin via .background(.ultraThinMaterial)).

♻️ Proposed refactor — use SwiftUI material instead of UIKit blur (iOS 15+, or keep UIKit for iOS 14)

If iOS 15+ is acceptable for blur, replace BackgroundBlurView with the built-in SwiftUI modifier:

-        .background(
-            ZStack {
-                BackgroundBlurView()
-                Color.dash.toastBackground
-            }
-        )
+        .background(
-            ZStack {
-                BackgroundBlurView()
-                Color.dash.toastBackground
-            }
+        )

Alternatively, if iOS 14 support is required, keep BackgroundBlurView but remove macOS 11 from the @available annotations to avoid confusion.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/DashUIKit/Components/Toast.swift` around lines 24 - 31, Resolve the
platform mismatch between BackgroundBlurView and Toast’s declared availability:
either add a SwiftUI-native macOS blur implementation and ensure Toast compiles
outside the UIKit guard, or remove macOS 11 from the `@available` annotations on
ToastStyle and Toast. Keep the existing UIKit blur for supported iOS versions if
iOS 14 compatibility is required.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Sources/DashUIKit/Components/MenuItem.swift`:
- Around line 112-114: Add the disabled state to the Toggle accessory in the
menu item’s accessory rendering logic: update the `.toggle(let isOn)` case to
apply `.disabled(!isEnabled)`, ensuring the switch cannot be changed when the
surrounding menu item is disabled.

In `@Sources/DashUIKit/Components/NavigationBar.swift`:
- Around line 96-117: Add localized accessibility labels to the icon-only button
created by NavigationBarElement.button(action:), providing distinct labels for
the back, close, plus, and info cases. Reuse the project’s established
localization/accessibility-label pattern and attach the label to the Button so
VoiceOver identifies each action.
- Around line 119-127: NavigationBarButtonStyle.makeBody uses the iOS 15-only
animation(_:value:) API despite the iOS 14 deployment target. Replace it with
the backward-compatible animation(_:) modifier, or conditionally apply the
value-based modifier with an iOS 15 availability check while preserving the
pressed-state animation.

In `@Sources/DashUIKit/Components/RadioButtonRow.swift`:
- Around line 54-101: Add the selection accessibility trait to the row’s Button
in RadioButtonRow.body by applying accessibilityAddTraits with .isSelected when
isSelected is true and no additional traits otherwise, so VoiceOver announces
the current selection state for both radio and checkbox styles.

In `@Sources/DashUIKit/Components/SystemMessageView.swift`:
- Around line 75-95: Update the button container condition in SystemMessageView
to render the HStack only when at least one complete button pair exists: button
name with its corresponding action, or secondary button name with its
corresponding action. Keep the existing inner DashButton conditions and padding
unchanged.

---

Nitpick comments:
In `@Sources/DashUIKit/Components/DashAmount.swift`:
- Around line 37-45: DashAmountFormat’s static numberFormatter captures
Locale.current only on first access, causing stale formatting after runtime
locale changes. Update the formatting logic to obtain or configure a
NumberFormatter with Locale.current for each formatting call, using
Formatter.withLocale(.current) where supported, and remove reliance on the
permanently cached locale in numberFormatter.

In `@Sources/DashUIKit/Components/SearchBar.swift`:
- Around line 48-214: Extract the duplicated magnifyingGlass and clearButton
implementations from SearchBarFocused and SearchBarLegacy into shared private
views, such as SearchBarMagnifyingGlass and SearchBarClearButton(text:),
preserving the existing icons, sizing, accessibility label, and binding
behavior; replace both structs’ computed properties with those shared
components.

In `@Sources/DashUIKit/Components/Toast.swift`:
- Around line 24-31: Resolve the platform mismatch between BackgroundBlurView
and Toast’s declared availability: either add a SwiftUI-native macOS blur
implementation and ensure Toast compiles outside the UIKit guard, or remove
macOS 11 from the `@available` annotations on ToastStyle and Toast. Keep the
existing UIKit blur for supported iOS versions if iOS 14 compatibility is
required.

In `@Sources/DashUIKit/Table` List/List1View.swift:
- Around line 20-29: In List1View, make the label and value stored properties
immutable by changing both public var declarations to public let, and remove the
placeholder default arguments from its initializer so callers must provide
explicit values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 152be8f6-976d-4edf-800e-c2e33c91afb0

📥 Commits

Reviewing files that changed from the base of the PR and between 19d5413 and 700b9ce.

⛔ Files ignored due to path filters (12)
  • Sources/DashUIKit/Resources/Media.xcassets/Icons & Illustrations/Menu/menu-receive.disabled.imageset/receive-disabled.png is excluded by !**/*.png
  • Sources/DashUIKit/Resources/Media.xcassets/Icons & Illustrations/Menu/menu-receive.disabled.imageset/receive-disabled@2x.png is excluded by !**/*.png
  • Sources/DashUIKit/Resources/Media.xcassets/Icons & Illustrations/Menu/menu-receive.disabled.imageset/receive-disabled@3x.png is excluded by !**/*.png
  • Sources/DashUIKit/Resources/Media.xcassets/Icons & Illustrations/Menu/menu-send.disabled.imageset/send-disabled.png is excluded by !**/*.png
  • Sources/DashUIKit/Resources/Media.xcassets/Icons & Illustrations/Menu/menu-send.disabled.imageset/send-disabled@2x.png is excluded by !**/*.png
  • Sources/DashUIKit/Resources/Media.xcassets/Icons & Illustrations/Menu/menu-send.disabled.imageset/send-disabled@3x.png is excluded by !**/*.png
  • Sources/DashUIKit/Resources/Media.xcassets/Icons & Illustrations/System message/info-rect.imageset/info-rect.png is excluded by !**/*.png
  • Sources/DashUIKit/Resources/Media.xcassets/Icons & Illustrations/System message/info-rect.imageset/info-rect@2x.png is excluded by !**/*.png
  • Sources/DashUIKit/Resources/Media.xcassets/Icons & Illustrations/System message/info-rect.imageset/info-rect@3x.png is excluded by !**/*.png
  • Sources/DashUIKit/Resources/Media.xcassets/Icons & Illustrations/Toast/toast-no-wifi.imageset/toast-no-wifi.png is excluded by !**/*.png
  • Sources/DashUIKit/Resources/Media.xcassets/Icons & Illustrations/Toast/toast-no-wifi.imageset/toast-no-wifi@2x.png is excluded by !**/*.png
  • Sources/DashUIKit/Resources/Media.xcassets/Icons & Illustrations/Toast/toast-no-wifi.imageset/toast-no-wifi@3x.png is excluded by !**/*.png
📒 Files selected for processing (15)
  • Sources/DashUIKit/Components/AddressFieldView.swift
  • Sources/DashUIKit/Components/BottomSheet.swift
  • Sources/DashUIKit/Components/DashAmount.swift
  • Sources/DashUIKit/Components/DashSwitch.swift
  • Sources/DashUIKit/Components/MenuItem.swift
  • Sources/DashUIKit/Components/NavigationBar.swift
  • Sources/DashUIKit/Components/RadioButtonRow.swift
  • Sources/DashUIKit/Components/SearchBar.swift
  • Sources/DashUIKit/Components/SystemMessageView.swift
  • Sources/DashUIKit/Components/Toast.swift
  • Sources/DashUIKit/Resources/Media.xcassets/Icons & Illustrations/Menu/menu-receive.disabled.imageset/Contents.json
  • Sources/DashUIKit/Resources/Media.xcassets/Icons & Illustrations/Menu/menu-send.disabled.imageset/Contents.json
  • Sources/DashUIKit/Resources/Media.xcassets/Icons & Illustrations/System message/info-rect.imageset/Contents.json
  • Sources/DashUIKit/Resources/Media.xcassets/Icons & Illustrations/Toast/toast-no-wifi.imageset/Contents.json
  • Sources/DashUIKit/Table List/List1View.swift

Comment thread Sources/DashUIKit/Components/MenuItem.swift
Comment thread Sources/DashUIKit/Components/NavigationBar.swift
Comment thread Sources/DashUIKit/Components/NavigationBar.swift
Comment thread Sources/DashUIKit/Components/RadioButtonRow.swift
Comment thread Sources/DashUIKit/Components/SystemMessageView.swift Outdated
- MenuItem: disable the toggle accessory when the row is disabled
- NavigationBar: add localized accessibility labels to back/close/plus/info
  icon-only buttons
- NavigationBar: gate animation(_:value:) (iOS 15+) behind if #available with an
  iOS 14 fallback
- RadioButtonRow: expose selection state to VoiceOver via accessibilityAddTraits
- SystemMessageView: only render the button container when a button actually
  has both a name and an action
@romchornyi
romchornyi merged commit 1a0f508 into master Jul 14, 2026
3 checks passed
@romchornyi
romchornyi deleted the import/core-components branch July 15, 2026 07:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants