Contents

amirsaam/Radix-UI-Swift

RadixUI ready to be used in SwiftUI (Unofficial)

Installation

  1. In Xcode, go to your Package Dependencies
  2. Put https://github.com/amirsaam/Radix-UI-Swift.git in Searchbar
  3. Change Dependency Rule to Up to Next Minor Version
  4. Set the lowest version to your desired version
  5. Press the Add Package button

Why `Minor`?

Since RadixUI for Swift will not have any breaking Semantic's Minor version, it is versioned as Epoch.Major.Minor 100 + Patch if it could've, it should've been vice versa as Epoch 100 + Major, as result to get the correct behaviour as Semantic Versioning, choosing the Up to Next Minor Version is the way to go.

Usage example

Import the package in any place you want to use it as:
```swift
import RadixUI
```

### Colors
You can use Radix Colors in 2 ways:
```swift
Text("RadixUI-Swift")
  .foregroundColor(.crimson9) // Directly
```
or
```swift
private var color: RadixAutoColor = .crimson

Text("RadixUI-Swift")
  .foregroundColor(color.solid9) // From RadixAutoColor Enum
```

In order to use your own custom color pallete head to [Radix Pallete Generator](https://www.radix-ui.com/colors/custom) and create your pallete and add them to your Asset Catalogue in 12 level named in this way for example: MyColor1, MyColor2, MyColor3, MyColor4, MyColor5, ... MyColor12 then using RadixAutoColor's `.custom` property:
```swift
private var color: RadixAutoColor = .custom("myColor")
// Reads all 12 levels of your custom pallete using the color's child variables.
// It will automatically capitalize the first letter for you.

Text("RadixUI-Swift")
  .foregroundColor(color.solid9)
```

**Important Note**: All RadixUI modifiers and styles are using `RadixAutoColor` enum

**Most Important Note**: All Radix theme styles have a default dynamic Black on Light ColorScheme and White on Dark ColorScheme, so

**DO NOT PASS `.whiteA` or `.blackA` TO RADIX MODIFIERS**

### Icons
Use icons in `Image` with bundle name completely customizable in two ways
```swift
// Default SwiftUI's Image
Image("github-logo", bundle: .radixUI)
  .resizable()
  .aspectRatio(contentMode: .fit)
  .frame(width: anySize, height: anySize)
  .foregroundColor(.ruby9)

// Built-in Custom Extension
ResizableBundledImage(imageName: "vercel-logo", imageSize: 20, bundle: .radixUI)
  .foregroundColor(.ruby9)
```
For using in `Label` use the built-in extension that uses `ResizableBundledImage`:
```swift
Label("Vercel", imageName: "vercel-logo", imageSize: 20, bundle: .radixUI)
```

### Avatar
To create an Radix-themed Avatar loader you can use this View that loads the image from url using built-in SwiftUI's `AsyncImage`:
```swift
// for person's name abbreviation placeholder/fallback, it will automatically abbreviate the full name
RadixAvatar(
    url: URL(string: "urlToImage"),
    name: "Passed Name",
    variant: .soft,
    size: .small,
    radius: .large,
    color: .grass
)
// or for person icon placeholder/fallback
RadixAvatar(
    url: URL(string: "urlToImage"),
    variant: .solid,
    size: .medium,
    radius: .none,
    color: .grass
)

// variant: soft, solid
// size: small, medium, large
// radius: none, large, full
```

### Badge
You can tranform any Text into a RadixUI badge:
```swift
Text("New")
    .font(.footnote)
    .radixBadge(variant: .outline, radius: .none, color: .grass)
// variant: outline, soft, solid, surface
// radius: none, large, full
```

### Button
RadixUI does have multiple ButtonStyles as: radixGhost, radixOutline, radixSoft, radixSolid and radixSurface

**Note 1**: RadixUI buttons can have a loading state after press that does change the Button label's icon into a progress view until the boolean becomes `false` again.
Examples:
```swift
@State private var isLoading = false

// Surely you can go for < Button {} label: {} > approach too!
Button(action: functionName) {
    // Best for using Label with RadixUI icons
    Label(LocalizedStringKey, imageName: String, imageSize: CGFloat, bundle: Bundle)
    // or create one
    Label {
        Text(LocalizedStringKey)
    } icon: {
        Image(String)
        Image(systemName: String)
        Image(String, bundle: Bundle?)
    }
    // or to use Asset Catalog Image and SFSymbols
    Label(LocalizedStringKey, image: String)
    Label(LocalizedStringKey, systemImage: String)
}
.buttonStyle(
// for having the similarity to RadixUI buttons like scale and opacity and etc;
// does not change anything of the button but adds some effects.
    .radixCustom()
// or
    .radixGhost(
        layout: .leading,
        radius: .large,
        color: .grass
    ),
    isLoading: $isLoading
// or
    .radixOutline(
        layout: .leading,
        radius: .large,
        color: .grass,
        // you can pass your custom size into the button in here or completely ignore it
        // in order to have it scaled based on button label's content
        frame: (maxWidth: maxWidth, minHeight: minHeight)
    ),
    isLoading: $isLoading
// or
    .radixSoft(
        layout: .leading,
        radius: .large,
        color: .grass
    ),
    isLoading: $isLoading
// or
    .radixSolid(
        layout: .leading,
        radius: .large,
        color: .grass
    ),
    isLoading: $isLoading
// or
    .radixSurface(
        layout: .leading,
        radius: .large,
        color: .grass
    )
// you completely ignore passing `isLoading` to the button style if you don't want to utilize the loading state
)
// layout: icon, title, leading, trailing
// radius: none, large, full
```
**Note 2**: Always pass a Label() as label of the button, do not pass Text or Image only, if you want only one of them use the `layout` variable's `.icon` or `.title` cases.

### Callout
You can tranform any Text into a RadixUI callout:
```swift
let infoText = "You will need admin privileges to install and access this application."
let alertText = "Access denied. Please contact the network administrator to view this page."

Text(infoText)
    .radixInfoCallout(variant: .surface, color: .grass) // info callout, customizable color, `info-circled` icon

Text(alertText)
    .radixAlertCallout(variant: .surface) // alert callout, red color, `exclamation-triangle` icon

// variant: outline, soft, surface
```

### RadioGroup
Under the hood it is just a looped Toggle but, it only allows one toggle to be on in the same time, needs to get passed an array of options that their model conforms to certain protocols
```swift
// the radio option data options should conform to these protocols
struct Option: Identifiable, Equatable {
    let id = UUID()
    let title: LocalizedStringKey
}

@State private var selectedOption: Option? = nil
private let options = [
    Option(title: "Option 1"),
    Option(title: "Option 2"),
    Option(title: "Option 3")
]

RadixRadioGroup(
    options: options, // pass the array of data options
    selected: $selectedOption, // bound of selected option
    style: (variant: .surface, layout: .trailing, color: .grass)
) {
// what the options should show as label
    Text($0.title)
        .foregroundStyle(.grass12)
}
// variant: .soft, surface
// layout: .leading, .trailing
```

### SegmentedControl
For customizing Segmented Picker of SwiftUI to match Radix style create an `init` in `@main` struct of the app to apply it globally or just apply such `init` in any view you want: 
```swift
struct PickerDemoView: View {

    init() {
        RadixSegmentedPicker(
            color: .grass,
            selectedFont: .systemFont(ofSize: 15, weight: .semibold),
            notSelectedFont: .systemFont(ofSize: 15, weight: .light)
        )
    }

    @State private var selected: RadixToggleType = .switch

    var body: some View {
        Picker(String(""), selection: $selected) {
            Text("Checkbox").tag(RadixToggleType.checkbox)
            Text("Radio").tag(RadixToggleType.radio)
            Text("Switch").tag(RadixToggleType.switch)
            Text("Toggle").tag(RadixToggleType.toggle)
            
        }
        .pickerStyle(.segmented)
        .padding()
        .frame(width: 400)
    }
}
```
**Important Note**: It does not work in macOS yet!

### Slider
Since SwiftUI's Slider does not accept styles like how button does, RadixUI for Swift has it's own RadixSlider View:
```swift
@State private var value = 5.0

RadixSlider(value: $value, step: 1, in: 0...10)
    .rxSliderStyle(
        .radixSoft(
            radius: .full,
            size: .medium,
            color: .grass
        )
// or
        .radixSurface(
            radius: .full,
            size: .medium,
            color: .grass
        )
    )
// radius: .none, .large, .full
// size: small, medium, large

```

### TextField
RadixUI for Swift TextFieldStyle just like ButtonStyle has a loading state that transforms `iconLabel` into a ProgressView also can have a action passing a function into it with or without a button that will show `iconButton` in trailing side that follows textfieldstyle's styling.
```swift
@State private var input = ""
@State private var isLoading = false

TextField("Placeholder1", text: $input)
    .textFieldStyle(
        .radixSurface(
            radius: .large,
            color: .grass, // optional, default .blue
            iconLabel: Image("quote", bundle: .radixUI), // optional
            iconButton: Image("arrow-right", bundle: .radixUI), // optional
            action: functionName // optional action, does provide .onSubmit by default to textfield
        ),
        isLoading: $isLoading
// or
        .radixSoft(
            radius: .large,
            color: .grass, // optional, default .blue
            iconLabel: Image("quote", bundle: .radixUI),  // optional image
            iconButton: Image("arrow-right", bundle: .radixUI),  // optional image
            action: functionName  // optional action, does provide .onSubmit by default to textfield
        ),
        isLoading: $isLoading
    )
// radius: .none, .large, .full
```

### Toast
This package has a toast or in-app notification functionality with RadixUI style following Apple Design Guidelines. It does get triggered by a `Binding<Bool>` variable, can have action button as a CTA or just a dismiss button, can be swiped to top or bot based on toast showing location.
```swift
@State private var presentInfoToast = false
@State private var presentActionToast = false

var body: some View {
    VStack { // << this is the highest container in the view, toast should applied to it
        HStack {

        }
    }
    .radixInfoToast( // this is a info toast, action is dismiss
        $presentInfoToast,
        variant: .surface,
        position: .bottom,
        color: .grass,
        duration: 3 // 0 == never, any other number auto dismisses the toast after the given number in seconds
    ) {
        Label(
            "This is a Radix Info Toast",
            imageName: "vercel-logo",
            imageSize: 20,
            bundle: .module
        ) // << pass a label to this part just like how explained in button part of this readme
    }
    .radixActionToast( // this is a action toast, you should define an action for it
        $presentActionToast,
        variant: .soft,
        position: .top,
        color: .grass,
        duration: 0
    ) {
        presentInfoToast = true // define the action you wnat here or just pass a function
    } buttonLabel: {
        Label(
            "Ignored Text",
            imageName: "avatar",
            imageSize: 20,
            bundle: .radixUI
        ) // pass a label as before for the button of the action, only shows the icon
    } toastLabel: {
        Label(
            "Button Presents Info Toast",
            imageName: "vercel-logo",
            imageSize: 20,
            bundle: .radixUI
        ) // pass a label as the toast's main content
    }
}
// variant: .soft, .surface
// position: .bottom, .top
```

### Toggle
RadixUI has 4 types of toggle styles, one of them is `radixRadio` that has been skipped in this part because its mainly used in `RadixRadioGroup` and using it as a togglestyle on one item is not a good approach
```swift
Toggle(isOn: $toggleBinding) {
    ResizableBundledImage( // showed after toggle (checkbox( has been turned on (checked)
        imageName: "check",
        imageSize: 20,
        bundle: .radixUI
    )
}
.toggleStyle(
    .radixCheckbox(
        variant: .surface,
        color: .grass
    )
)
Toggle(isOn: $toggleBinding) {
    Text("Label Text")
        .foregroundStyle(radixColor.grass.text2)
}
.toggleStyle(
    .radixSwitch( // a switch style, just like swiftui's default with styling of radix ui
        variant: .surface,
        radius: .full,
        color: .grass
    )
)
Toggle(isOn: $toggleBinding) {
    ResizableBundledImage(
        imageName: "font-italic",
        imageSize: 20,
        bundle: .radixUI
    )
}
.toggleStyle(
    .radixToggle( // toggle as toggle, dimmer color when off, strong color when on
        color: .grass
    )
)
// variant: .soft, .surface
// radius: .none, .large, .full
```

### Shadow
Radix Shadows are available as `ViewModifier` in 6 level:
```swift
AnyView
.radixShadow1()
.radixShadow2()
.radixShadow3()
.radixShadow4()
.radixShadow5()
.radixShadow6()
```

To Do

Main:

Help needed on these, any PR is welcomed:

  • [ ] Add Ranged Slider (Two Thumbs)
  • [ ] Add classic variant for components that have it
  • [ ] Add Progress Bar
  • [ ] Add Skeleton
  • [ ] Improve Shadows

Dependencies

  • RadixUI-Swift has no dependency

Meta

Amir Mohammadi – @amirsaam – amirsaam [at] me [dot] com

Distributed under the MIT license. See `LICENSE` for more information.

https://github.com/amirsaam/RadixUI-Swift

Credits

[swift-image]: https://badgen.net/static/Swift/5.5/orange [swift-url]: https://swift.org/

[latest-tag-image]: https://badgen.net/github/tag/amirsaam/radix-ui-swift [latest-stable-image]: https://badgen.net/github/release/amirsaam/Radix-UI-Swift/stable [releases-url]: https://github.com/amirsaam/Radix-UI-Swift/releases

[last-commit-image]: https://badgen.net/github/last-commit/amirsaam/Radix-UI-Swift [commits-url]: https://github.com/amirsaam/Radix-UI-Swift/commits/main/

[contributors-image]: https://badgen.net/github/contributors/amirsaam/Radix-UI-Swift [contributors-url]: https://github.com/amirsaam/Radix-UI-Swift/graphs/contributors

[stars-image]: https://badgen.net/github/stars/amirsaam/Radix-UI-Swift [forks-image]: https://badgen.net/github/forks/amirsaam/Radix-UI-Swift

[license-image]: https://badgen.net/github/license/amirsaam/Radix-UI-Swift [license-url]: LICENSE

Package Metadata

Repository: amirsaam/Radix-UI-Swift

Homepage: https://www.radix-ui.com

Stars: 5

Forks: 1

Open issues: 0

Default branch: main

Primary language: swift

License: MIT

Topics: accessibility, color-palette, design-system, iconset, radix-colors, radix-icons, radix-ui, svg, svg-icons, swift, swift-package, swiftui, ui, ui-library

README: README.md