diff --git a/.github/workflows/ci-pull-request.yml b/.github/workflows/ci-pull-request.yml
deleted file mode 100644
index ba40e0e..0000000
--- a/.github/workflows/ci-pull-request.yml
+++ /dev/null
@@ -1,66 +0,0 @@
-name: CI - Pull Request
-on:
- pull_request:
- branches:
- - main
- push:
- branches:
- - develop
- workflow_dispatch:
-
-jobs:
- build-swiftpm:
- name: Build SwiftPM
- uses: oversizedev/GithubWorkflows/.github/workflows/build-swiftpm.yml@main
- with:
- package: OversizeUI
- secrets: inherit
-
- build-iOS-example:
- name: Build iOS example
- needs: build-swiftpm
- uses: oversizedev/GithubWorkflows/.github/workflows/build-app.yml@main
- strategy:
- matrix:
- destination: ['platform=iOS Simulator,name=iPhone 15 Pro,OS=17.2', 'platform=iOS Simulator,name=iPad Pro (12.9-inch) (6th generation),OS=17.2']
- with:
- path: Example/Example
- scheme: Example (iOS)
- destination: ${{ matrix.destination }}
- secrets: inherit
-
- build-macOS-example:
- name: Build macOS examples
- needs: build-swiftpm
- uses: oversizedev/GithubWorkflows/.github/workflows/build-app.yml@main
- with:
- path: Example/Example
- scheme: Example (macOS)
- destination: platform=macOS,arch=arm64
- secrets: inherit
-
- build-tvOS-example:
- name: Build tvOS examples
- needs: build-swiftpm
- uses: oversizedev/GithubWorkflows/.github/workflows/build-app.yml@main
- strategy:
- matrix:
- destination: ['platform=tvOS Simulator,name=Apple TV 4K (3rd generation) (at 1080p),OS=17.2', 'platform=tvOS Simulator,name=Apple TV 4K (3rd generation) (at 1080p),OS=16.4']
- with:
- path: Example/Example
- scheme: Example (tvOS)
- destination: ${{ matrix.destination }}
- secrets: inherit
-
- build-watchOS-example:
- name: Build watchOS examples
- needs: build-swiftpm
- uses: oversizedev/GithubWorkflows/.github/workflows/build-app.yml@main
- strategy:
- matrix:
- destination: ['platform=watchOS Simulator,name=Apple Watch SE (44mm) (2nd generation),OS=10.5']
- with:
- path: Example/Example
- scheme: Example (watchOS)
- destination: ${{ matrix.destination }}
- secrets: inherit
diff --git a/.github/workflows/ci-release.yml b/.github/workflows/ci.yml
similarity index 61%
rename from .github/workflows/ci-release.yml
rename to .github/workflows/ci.yml
index 6bc476a..ddb0340 100644
--- a/.github/workflows/ci-release.yml
+++ b/.github/workflows/ci.yml
@@ -1,10 +1,13 @@
-name: CI - Release
+name: CI
on:
pull_request:
types:
- closed
branches:
- main
+ push:
+ branches:
+ - '**'
workflow_dispatch:
jobs:
@@ -13,9 +16,14 @@ jobs:
uses: oversizedev/GithubWorkflows/.github/workflows/build-swiftpm.yml@main
strategy:
matrix:
- packages: [OversizeUI]
+ destination:
+ - platform=iOS Simulator,name=iPhone 16,OS=18.0
+ - platform=watchOS Simulator,name=Apple Watch SE (40mm) (2nd generation),OS=11.0
+ - platform=tvOS Simulator,name=Apple TV 4K (3rd generation) (at 1080p),OS=18.0
+ - platform=macOS,arch=arm64
with:
- package: ${{ matrix.packages }}
+ package: OversizeUI
+ destination: ${{ matrix.destination }}
secrets: inherit
build-iOS-example:
@@ -24,7 +32,9 @@ jobs:
uses: oversizedev/GithubWorkflows/.github/workflows/build-app.yml@main
strategy:
matrix:
- destination: ['platform=iOS Simulator,name=iPhone 15 Pro,OS=17.2', 'platform=iOS Simulator,name=iPad Pro (12.9-inch) (6th generation),OS=17.2']
+ destination:
+ - platform=iOS Simulator,name=iPhone 16 Pro,OS=18.0
+ - platform=iOS Simulator,name=iPad (10th generation),OS=18.0
with:
path: Example/Example
scheme: Example (iOS)
@@ -47,7 +57,9 @@ jobs:
uses: oversizedev/GithubWorkflows/.github/workflows/build-app.yml@main
strategy:
matrix:
- destination: ['platform=tvOS Simulator,name=Apple TV 4K (3rd generation) (at 1080p),OS=17.2', 'platform=tvOS Simulator,name=Apple TV 4K (3rd generation) (at 1080p),OS=16.4']
+ destination:
+ - platform=tvOS Simulator,name=Apple TV 4K (3rd generation) (at 1080p),OS=18.0
+ - platform=tvOS Simulator,name=Apple TV 4K (3rd generation),OS=18.0
with:
path: Example/Example
scheme: Example (tvOS)
@@ -60,22 +72,23 @@ jobs:
uses: oversizedev/GithubWorkflows/.github/workflows/build-app.yml@main
strategy:
matrix:
- destination: ['platform=watchOS Simulator,name=Apple Watch SE (44mm) (2nd generation),OS=10.5']
+ destination:
+ - platform=watchOS Simulator,name=Apple Watch SE (40mm) (2nd generation),OS=11.0
with:
path: Example/Example
scheme: Example (watchOS)
destination: ${{ matrix.destination }}
secrets: inherit
-
+
bump:
name: Bump version
- needs: [build-swiftpm, build-iOS-example, build-macOS-example, build-tvOS-example, build-watchOS-example]
+ if: github.ref == 'refs/heads/main'
+ needs:
+ - build-swiftpm
+ - build-iOS-example
+ - build-macOS-example
+ - build-tvOS-example
+ - build-watchOS-example
+
uses: oversizedev/GithubWorkflows/.github/workflows/bump.yml@main
secrets: inherit
-
- publish-docc:
- name: Publish docc
- needs: bump
- uses: ./.github/workflows/publish-docc.yml
- secrets: inherit
-
diff --git a/.github/workflows/publish-docc.yml b/.github/workflows/publish-docc.yml
deleted file mode 100644
index d461258..0000000
--- a/.github/workflows/publish-docc.yml
+++ /dev/null
@@ -1,37 +0,0 @@
-name: Deploy DocC
-on:
- workflow_dispatch:
- workflow_call:
-permissions:
- contents: read
- pages: write
- id-token: write
-concurrency:
- group: "pages"
- cancel-in-progress: true
-jobs:
- deploy:
- environment:
- name: github-pages
- url: ${{ steps.deployment.outputs.page_url }}
- runs-on: macos-14
- steps:
- - name: Checkout 🛎️
- uses: actions/checkout@v3
- - name: Build DocC
- run: |
- xcodebuild docbuild -scheme -skipPackagePluginValidation OversizeUI \
- -derivedDataPath /tmp/docbuild \
- -destination 'generic/platform=iOS';
- $(xcrun --find docc) process-archive \
- transform-for-static-hosting /tmp/docbuild/Build/Products/Debug-iphoneos/OversizeUI.doccarchive \
- --hosting-base-path OversizeUI \
- --output-path docs;
- echo "" > docs/index.html;
- - name: Upload artifact
- uses: actions/upload-pages-artifact@v1
- with:
- path: 'docs'
- - name: Deploy to GitHub Pages
- id: deployment
- uses: actions/deploy-pages@v1
diff --git a/README.md b/README.md
index b9d7f64..054c8d3 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# OversizeUI
-[![Build Example](https://github.com/oversizedev/OversizeUI/actions/workflows/build-example.yml/badge.svg)](https://github.com/oversizedev/OversizeUI/actions/workflows/build-example.yml) [![Deploy DocC](https://github.com/oversizedev/OversizeUI/actions/workflows/publish-docc.yml/badge.svg)](https://github.com/oversizedev/OversizeUI/actions/workflows/publish-docc.yml)
+[![Build Example](https://github.com/oversizedev/OversizeUI/actions/workflows/build-example.yml/badge.svg)](https://github.com/oversizedev/OversizeUI/actions/workflows/ci-release.yml)
Yet another component library on SwiftUI
diff --git a/Sources/OversizeUI/Controls/ContentView/ContentView.swift b/Sources/OversizeUI/Controls/ContentView/ContentView.swift
index f7290bd..93b4d0f 100644
--- a/Sources/OversizeUI/Controls/ContentView/ContentView.swift
+++ b/Sources/OversizeUI/Controls/ContentView/ContentView.swift
@@ -88,7 +88,6 @@ extension ContentView {
#if os(macOS)
.controlSize(.large)
#endif
-
case .back:
Button(action: { dismiss() }) {
@@ -247,7 +246,6 @@ extension ContentView {
.buttonStyle(.tertiary)
#endif
.disabled(true)
-
case .none:
EmptyView()
}
diff --git a/Sources/OversizeUI/Controls/Divider/Separator.swift b/Sources/OversizeUI/Controls/Divider/Separator.swift
index cc1f04c..fb7f655 100644
--- a/Sources/OversizeUI/Controls/Divider/Separator.swift
+++ b/Sources/OversizeUI/Controls/Divider/Separator.swift
@@ -24,24 +24,40 @@ public struct Separator: View {
return Rectangle()
.fill(Color.border)
- .frame(width: isHorizontal ? nil : 0.5,
- height: isHorizontal ? 0.5 : nil)
+ .frame(
+ width: isHorizontal ? nil : lineWidth,
+ height: isHorizontal ? lineWidth : nil
+ )
+ #if !os(macOS)
.padding(insets(isHorizontal))
+ #endif
}
private func insets(_ isHorizontal: Bool) -> EdgeInsets {
if isHorizontal {
- EdgeInsets(top: 0.5,
- leading: padding.rawValue,
- bottom: 0,
- trailing: padding.rawValue)
+ EdgeInsets(
+ top: 0.5,
+ leading: padding.rawValue,
+ bottom: 0,
+ trailing: padding.rawValue
+ )
} else {
- EdgeInsets(top: padding.rawValue,
- leading: 0.5,
- bottom: padding.rawValue,
- trailing: 0)
+ EdgeInsets(
+ top: padding.rawValue,
+ leading: 0.5,
+ bottom: padding.rawValue,
+ trailing: 0
+ )
}
}
+
+ var lineWidth: CGFloat {
+ #if os(macOS)
+ return 1
+ #else
+ return 0.5
+ #endif
+ }
}
struct Separator_Preview: PreviewProvider {
diff --git a/Sources/OversizeUI/Controls/PageControl/PageIndexView.swift b/Sources/OversizeUI/Controls/PageControl/PageIndexView.swift
index b549095..72d48c3 100644
--- a/Sources/OversizeUI/Controls/PageControl/PageIndexView.swift
+++ b/Sources/OversizeUI/Controls/PageControl/PageIndexView.swift
@@ -6,11 +6,11 @@
import SwiftUI
public struct PageIndexView: View {
- @Binding private var index: Int
+ private var index: Int
private let maxIndex: Int
- public init(_ index: Binding, maxIndex: Int) {
- _index = index
+ public init(_ index: Int, maxIndex: Int) {
+ self.index = index
self.maxIndex = maxIndex
}
diff --git a/Sources/OversizeUI/Controls/PriceField/PriceField.swift b/Sources/OversizeUI/Controls/PriceField/PriceField.swift
index 3d1a48b..8495391 100644
--- a/Sources/OversizeUI/Controls/PriceField/PriceField.swift
+++ b/Sources/OversizeUI/Controls/PriceField/PriceField.swift
@@ -22,17 +22,16 @@ public struct PriceField: View {
return formatter
}
- private var strValue2: String { value.components(separatedBy: CharacterSet(charactersIn: "0123456789").inverted).joined() } // value.filter { !$0.isWhitespace } }
- private var doubleValue2: Double { .init((Double(strValue2) ?? 0) / 100.0) ?? 0 }
+ private var stringValue2: String { value.components(separatedBy: CharacterSet(charactersIn: "0123456789").inverted).joined() }
+ private var doubleValue2: Double { .init((Double(stringValue2) ?? 0) / 100.0) ?? 0 }
- private var strValue3: String { value.components(separatedBy: CharacterSet(charactersIn: "0123456789").inverted).joined() } // value.filter { !$0.isWhitespace } }
- private var doubleValue3: Double { .init((Double(strValue3) ?? 0) * 100) ?? 0 }
+ private var stringValue3: String { value.components(separatedBy: CharacterSet(charactersIn: "0123456789").inverted).joined() }
+ private var doubleValue3: Double { .init((Double(stringValue3) ?? 0) * 100) ?? 0 }
private var strValue: String { value.filter { !$0.isWhitespace } }
private var doubleValue: Double { .init(strValue) ?? 0 }
private var formattedValue: String {
let value = doubleValue
-
return formatter.string(for: value) ?? ""
}
@@ -80,7 +79,6 @@ public struct PriceField: View {
.padding(.leading, .xSmall)
Text(dispalySymbol)
-
.onChange(of: currency) { _ in
validate()
}
diff --git a/Sources/OversizeUI/Controls/Row/Row.swift b/Sources/OversizeUI/Controls/Row/Row.swift
index f04c98f..95f0e65 100644
--- a/Sources/OversizeUI/Controls/Row/Row.swift
+++ b/Sources/OversizeUI/Controls/Row/Row.swift
@@ -47,12 +47,13 @@ public struct Row: View where LeadingLabel: View, T
(subtitle?.isEmpty) != nil
}
- public init(_ title: String,
- subtitle: String? = nil,
- action: (() -> Void)? = nil,
- @ViewBuilder leading: () -> LeadingLabel,
- @ViewBuilder trailing: () -> TrailingLabel)
- {
+ public init(
+ _ title: String,
+ subtitle: String? = nil,
+ action: (() -> Void)? = nil,
+ @ViewBuilder leading: () -> LeadingLabel,
+ @ViewBuilder trailing: () -> TrailingLabel
+ ) {
self.title = title
self.subtitle = subtitle
self.action = action
diff --git a/Sources/OversizeUI/Controls/ScrollView/ScrollViewWithOffset.swift b/Sources/OversizeUI/Controls/ScrollView/ScrollViewWithOffset.swift
index 4c87040..ecf8804 100644
--- a/Sources/OversizeUI/Controls/ScrollView/ScrollViewWithOffset.swift
+++ b/Sources/OversizeUI/Controls/ScrollView/ScrollViewWithOffset.swift
@@ -6,6 +6,14 @@
import SwiftUI
public struct ScrollViewWithOffsetTracking: View {
+ public typealias ScrollAction = (_ offset: CGPoint) -> Void
+
+ private let axes: Axis.Set
+ private let showsIndicators: Bool
+ private let cordinateSpaceName: String
+ private let onScroll: ScrollAction
+ private let content: () -> Content
+
public init(
_ axes: Axis.Set = .vertical,
showsIndicators: Bool = true,
@@ -20,14 +28,6 @@ public struct ScrollViewWithOffsetTracking: View {
self.content = content
}
- public typealias ScrollAction = (_ offset: CGPoint) -> Void
-
- private let axes: Axis.Set
- private let showsIndicators: Bool
- private let cordinateSpaceName: String
- private let onScroll: ScrollAction
- private let content: () -> Content
-
public var body: some View {
ScrollView(axes, showsIndicators: showsIndicators) {
ScrollViewOffsetTracker {
diff --git a/Sources/OversizeUI/Controls/Surface/Surface.swift b/Sources/OversizeUI/Controls/Surface/Surface.swift
index 39ede1c..cd13ba9 100644
--- a/Sources/OversizeUI/Controls/Surface/Surface.swift
+++ b/Sources/OversizeUI/Controls/Surface/Surface.swift
@@ -69,17 +69,29 @@ public struct Surface: View {
.padding(.leading, forceContentInsets?.leading ?? contentInsets.leading)
.padding(.trailing, forceContentInsets?.trailing ?? contentInsets.trailing)
.background(
- RoundedRectangle(cornerRadius: surfaceRadius, style: .continuous)
- .fill(surfaceBackgroundColor)
- .overlay(
- RoundedRectangle(cornerRadius: surfaceRadius, style: .continuous)
- .strokeBorder(strokeBorderColor, lineWidth: strokeBorderLineWidth)
- )
- .shadowElevaton(elevation)
+ RoundedRectangle(
+ cornerRadius: surfaceRadius,
+ style: .continuous
+ )
+ .fill(surfaceBackgroundColor)
+ .shadowElevaton(elevation)
+ )
+ .overlay(
+ RoundedRectangle(
+ cornerRadius: surfaceRadius,
+ style: .continuous
+ )
+ .strokeBorder(
+ strokeBorderColor,
+ lineWidth: strokeBorderLineWidth
+ )
)
.if(isSurfaceClipped) { view in
view.clipShape(
- RoundedRectangle(cornerRadius: surfaceRadius, style: .continuous)
+ RoundedRectangle(
+ cornerRadius: surfaceRadius,
+ style: .continuous
+ )
)
}
}
diff --git a/Sources/OversizeUI/Controls/TextField/FieldHelperViewModifier.swift b/Sources/OversizeUI/Controls/TextField/FieldHelperViewModifier.swift
index 78672a2..fa1edce 100644
--- a/Sources/OversizeUI/Controls/TextField/FieldHelperViewModifier.swift
+++ b/Sources/OversizeUI/Controls/TextField/FieldHelperViewModifier.swift
@@ -14,6 +14,7 @@ public enum FieldHelperStyle {
public struct FieldHelperViewModifier: ViewModifier {
@Environment(\.theme) private var theme: ThemeSettings
+ @Environment(\.platform) private var platform: Platform
@Binding public var helperText: String
@Binding public var helperStyle: FieldHelperStyle
public init(helperText: Binding, helperStyle: Binding) {
@@ -22,24 +23,29 @@ public struct FieldHelperViewModifier: ViewModifier {
}
public func body(content: Content) -> some View {
- VStack(alignment: .leading) {
+ VStack(alignment: .leading, spacing: platform == .mac ? .xxxSmall : .xSmall) {
content
- if helperText != "" {
- if helperStyle == .helperText {
- Text(helperText)
- .subheadline(.semibold)
- .foregroundColor(.onSurfaceMediumEmphasis)
- } else if helperStyle == .errorText {
- Text(helperText)
- .subheadline(.semibold)
- .foregroundColor(.error)
- } else if helperStyle == .sussesText {
- Text(helperText)
- .subheadline(.semibold)
- .foregroundColor(.success)
- }
+ if helperText.isEmpty == false, helperStyle != .none {
+ Text(helperText)
+ .subheadline(.medium)
+ .foregroundColor(helperForegroundColor)
+ .offset(x: platform == .mac ? 4 : 0)
}
}
+ .animation(.easeIn(duration: 0.15), value: helperStyle)
+ }
+
+ private var helperForegroundColor: Color {
+ switch helperStyle {
+ case .helperText:
+ Color.onSurfaceMediumEmphasis
+ case .errorText:
+ Color.error
+ case .sussesText:
+ Color.success
+ case .none:
+ Color.clear
+ }
}
}
diff --git a/Sources/OversizeUI/Controls/TextField/LabeledTextFieldStyle.swift b/Sources/OversizeUI/Controls/TextField/LabeledTextFieldStyle.swift
index 3972a36..a88d016 100644
--- a/Sources/OversizeUI/Controls/TextField/LabeledTextFieldStyle.swift
+++ b/Sources/OversizeUI/Controls/TextField/LabeledTextFieldStyle.swift
@@ -10,7 +10,8 @@ public struct LabeledTextFieldStyle: TextFieldStyle {
@Environment(\.theme) private var theme: ThemeSettings
@Environment(\.fieldLabelPosition) private var fieldPlaceholderPosition: FieldLabelPosition
@Environment(\.fieldPosition) private var fieldPosition: FieldPosition
- @FocusState var isFocused: Bool
+ @Environment(\.platform) private var platform: Platform
+ @FocusState private var isFocused: Bool
@Binding private var text: String
private let placeholder: String
@@ -20,58 +21,112 @@ public struct LabeledTextFieldStyle: TextFieldStyle {
}
public func _body(configuration: TextField) -> some View {
- VStack(alignment: .leading, spacing: .xSmall) {
+ VStack(alignment: .leading, spacing: platform == .mac ? .xxxSmall : .xSmall) {
if fieldPlaceholderPosition == .adjacent {
- HStack {
- Text(placeholder)
- .subheadline(.medium)
- .foregroundColor(.onSurfaceHighEmphasis)
- Spacer()
- }
+ Text(placeholder)
+ .subheadline(.medium)
+ .foregroundColor(platform == .mac ? .onSurfaceMediumEmphasis : .onSurfaceHighEmphasis)
+ .frame(maxWidth: .infinity, alignment: .leading)
+ .offset(x: platform == .mac ? 4 : 0)
}
ZStack(alignment: .leading) {
labelTextView
configuration
.headline(.medium)
.foregroundColor(.onSurfaceHighEmphasis)
+ .padding(padding)
.offset(y: fieldOffset)
.focused($isFocused)
#if os(macOS)
- .textFieldStyle(.plain)
- .padding(.xxSmall)
- #else
- .padding(.vertical, fieldPlaceholderPosition == .overInput ? .xxxSmall : .zero)
- .padding()
-
+ .if(fieldPlaceholderPosition == .adjacent) {
+ $0.textFieldStyle(.roundedBorder).controlSize(.large)
+ }
+ .if(fieldPlaceholderPosition != .adjacent) {
+ $0.textFieldStyle(.plain)
+ }
#endif
}
.background(fieldBackground)
.overlay(overlay)
}
- .animation(.easeIn(duration: 0.15), value: text)
+ .animation(.easeIn(duration: 0.10), value: text)
+ }
+
+ private var padding: EdgeInsets {
+ switch fieldPlaceholderPosition {
+ case .default:
+ #if os(macOS)
+ return .init(.xxSmall)
+ #else
+ return .init(.small)
+ #endif
+ case .overInput:
+ #if os(macOS)
+ return .init(horizontal: .xxSmall, vertical: .xSmall)
+ #else
+ return .init(
+ top: Space.xxxSmall.rawValue + Space.small.rawValue,
+ leading: Space.small.rawValue,
+ bottom: Space.xxxSmall.rawValue + Space.small.rawValue,
+ trailing: Space.small.rawValue
+ )
+ #endif
+ case .adjacent:
+ #if os(macOS)
+ return .init(.zero)
+ #else
+ return .init(.small)
+ #endif
+ }
}
@ViewBuilder
private var fieldBackground: some View {
switch fieldPosition {
case .default:
+ #if canImport(UIKit)
RoundedRectangle(
- cornerRadius: Radius.medium,
+ cornerRadius: fieldRadius,
style: .continuous
)
.fill(isFocused ? Color.surfacePrimary : Color.surfaceSecondary)
+ #else
+ if fieldPlaceholderPosition != .adjacent {
+ RoundedRectangle(
+ cornerRadius: fieldRadius,
+ style: .continuous
+ )
+ .fill(isFocused ? Color.surfacePrimary : Color.surfaceSecondary)
+ }
+ #endif
case .top, .bottom, .center:
- #if os(iOS)
- RoundedRectangleCorner(radius: Radius.medium, corners: backgroundShapeCorners)
+ #if canImport(UIKit)
+ RoundedRectangleCorner(
+ radius: fieldRadius,
+ corners: backgroundShapeCorners
+ )
+ .fill(isFocused ? Color.surfacePrimary : Color.surfaceSecondary)
+ #else
+ if fieldPlaceholderPosition != .adjacent {
+ RoundedRectangle(
+ cornerRadius: fieldRadius,
+ style: .continuous
+ )
.fill(isFocused ? Color.surfacePrimary : Color.surfaceSecondary)
+ }
#endif
}
}
- #if os(iOS)
- @available(macOS, unavailable)
- @available(watchOS, unavailable)
- @available(tvOS, unavailable)
+ var fieldRadius: Radius {
+ #if os(macOS)
+ return .xSmall
+ #else
+ return .medium
+ #endif
+ }
+
+ #if canImport(UIKit)
private var backgroundShapeCorners: UIRectCorner {
switch fieldPosition {
case .default:
@@ -88,12 +143,14 @@ public struct LabeledTextFieldStyle: TextFieldStyle {
private var fieldOffset: CGFloat {
switch fieldPlaceholderPosition {
- case .default:
- 0
- case .adjacent:
- 0
+ case .default, .adjacent:
+ .zero
case .overInput:
+ #if os(macOS)
+ text.isEmpty ? 0 : 8
+ #else
text.isEmpty ? 0 : 10
+ #endif
}
}
@@ -101,21 +158,37 @@ public struct LabeledTextFieldStyle: TextFieldStyle {
private var overlay: some View {
switch fieldPosition {
case .default:
+ #if canImport(UIKit)
RoundedRectangle(
- cornerRadius: Radius.medium,
+ cornerRadius: fieldRadius,
style: .continuous
)
.stroke(overlayBorderColor, lineWidth: isFocused ? 2 : CGFloat(theme.borderSize))
- case .top, .bottom, .center:
- #if os(iOS)
- RoundedRectangleCorner(radius: Radius.medium, corners: backgroundShapeCorners)
+ #else
+ if fieldPlaceholderPosition != .adjacent {
+ RoundedRectangle(
+ cornerRadius: fieldRadius,
+ style: .continuous
+ )
.stroke(overlayBorderColor, lineWidth: isFocused ? 2 : CGFloat(theme.borderSize))
+ }
+ #endif
+
+ case .top, .bottom, .center:
+ #if canImport(UIKit)
+ RoundedRectangleCorner(radius: fieldRadius, corners: backgroundShapeCorners)
+ .stroke(
+ overlayBorderColor,
+ lineWidth: isFocused ? 2 : CGFloat(theme.borderSize)
+ )
#else
- RoundedRectangle(
- cornerRadius: Radius.medium,
- style: .continuous
- )
- .stroke(overlayBorderColor, lineWidth: isFocused ? 2 : CGFloat(theme.borderSize))
+ if fieldPlaceholderPosition != .adjacent {
+ RoundedRectangle(
+ cornerRadius: fieldRadius,
+ style: .continuous
+ )
+ .stroke(overlayBorderColor, lineWidth: isFocused ? 2 : CGFloat(theme.borderSize))
+ }
#endif
}
}
@@ -129,7 +202,11 @@ public struct LabeledTextFieldStyle: TextFieldStyle {
.subheadline()
.onSurfaceDisabledForegroundColor()
.opacity(0.7)
+ #if os(macOS)
+ .padding(.xSmall)
+ #else
.padding(.small)
+ #endif
}
case .adjacent:
EmptyView()
@@ -138,8 +215,13 @@ public struct LabeledTextFieldStyle: TextFieldStyle {
.font(text.isEmpty ? .headline : .subheadline)
.fontWeight(text.isEmpty ? .medium : .semibold)
.onSurfaceDisabledForegroundColor()
+ #if os(macOS)
+ .padding(.xSmall)
+ .offset(y: text.isEmpty ? 0 : -10)
+ #else
.padding(.small)
.offset(y: text.isEmpty ? 0 : -13)
+ #endif
.opacity(text.isEmpty ? 0 : 1)
}
}
diff --git a/Sources/OversizeUI/Controls/URLField/URLField.swift b/Sources/OversizeUI/Controls/URLField/URLField.swift
index c3a42f3..341c6de 100644
--- a/Sources/OversizeUI/Controls/URLField/URLField.swift
+++ b/Sources/OversizeUI/Controls/URLField/URLField.swift
@@ -7,6 +7,7 @@ import SwiftUI
#if os(iOS) || os(macOS)
@available(iOS 15.0, *)
+@available(macOS 14.0, *)
@available(watchOS, unavailable)
@available(tvOS, unavailable)
public struct URLField: View {
@@ -37,7 +38,7 @@ public struct URLField: View {
if state {
textFieldHelper = .none
- } else if let url = URL(string: urlString) {
+ } else if let url = URL(string: urlString), NSWorkspace.shared.urlForApplication(toOpen: url) != nil {
textFieldHelper = .none
self.url = url
} else {
@@ -55,7 +56,7 @@ public struct URLField: View {
textFieldHelper = .errorText
}
#else
- if let url = URL(string: urlString) {
+ if let url = URL(string: urlString), NSWorkspace.shared.urlForApplication(toOpen: url) != nil {
textFieldHelper = .none
self.url = url
} else {
@@ -66,8 +67,8 @@ public struct URLField: View {
#if os(iOS)
.keyboardType(.URL)
.textInputAutocapitalization(.never)
- .textContentType(.URL)
#endif
+ .textContentType(.URL)
.autocorrectionDisabled()
.fieldHelper(.constant("Invalid URL"), style: $textFieldHelper)
}
diff --git a/Sources/OversizeUI/Core/EdgeSpaceInsets.swift b/Sources/OversizeUI/Core/EdgeSpaceInsets.swift
index 9419a6f..f3fcf0b 100644
--- a/Sources/OversizeUI/Core/EdgeSpaceInsets.swift
+++ b/Sources/OversizeUI/Core/EdgeSpaceInsets.swift
@@ -24,4 +24,11 @@ public struct EdgeSpaceInsets {
bottom = vertical
trailing = horizontal
}
+
+ public init(_ all: Space) {
+ top = all
+ leading = all
+ bottom = all
+ trailing = all
+ }
}
diff --git a/Sources/OversizeUI/Core/EnvironmentKeys/Platform.swift b/Sources/OversizeUI/Core/EnvironmentKeys/Platform.swift
new file mode 100644
index 0000000..853b23b
--- /dev/null
+++ b/Sources/OversizeUI/Core/EnvironmentKeys/Platform.swift
@@ -0,0 +1,53 @@
+//
+// Copyright © 2024 Alexander Romanov
+// Platform.swift, created on 07.09.2024
+//
+
+import SwiftUI
+#if canImport(UIKit)
+import UIKit
+#endif
+
+public enum Platform {
+ case iPhone, iPad, mac, tv, watch, vision, carPlay, other
+}
+
+private struct PlatformKey: EnvironmentKey {
+ static let defaultValue: Platform = {
+ #if os(macOS) || targetEnvironment(macCatalyst)
+ return .mac
+ #elseif os(watchOS)
+ return .watch
+ #elseif os(visionOS)
+ return .vision
+ #elseif canImport(UIKit)
+ switch UIDevice.current.userInterfaceIdiom {
+ case .phone:
+ return .iPhone
+ case .pad:
+ return .iPad
+ case .tv:
+ return .tv
+ case .carPlay:
+ return .carPlay
+ case .mac:
+ return .mac
+ case .vision:
+ return .vision
+ case .unspecified:
+ return .other
+ @unknown default:
+ return .other
+ }
+ #else
+ return .other
+ #endif
+ }()
+}
+
+public extension EnvironmentValues {
+ var platform: Platform {
+ get { self[PlatformKey.self] }
+ set { self[PlatformKey.self] = newValue }
+ }
+}
diff --git a/Sources/OversizeUI/Core/Radius.swift b/Sources/OversizeUI/Core/Radius.swift
index 947f6c4..51d37e3 100644
--- a/Sources/OversizeUI/Core/Radius.swift
+++ b/Sources/OversizeUI/Core/Radius.swift
@@ -12,6 +12,8 @@ public enum Radius {
/// 0
case zero
+ /// 4
+ case xSmall
/// 8
case small
/// 12
@@ -25,6 +27,8 @@ public enum Radius {
switch self {
case .zero:
.zero
+ case .xSmall:
+ CGFloat(theme.radius / 2)
case .small:
CGFloat(theme.radius)
case .medium:
diff --git a/Sources/OversizeUI/Core/Space.swift b/Sources/OversizeUI/Core/Space.swift
index 1b193f0..4704e29 100644
--- a/Sources/OversizeUI/Core/Space.swift
+++ b/Sources/OversizeUI/Core/Space.swift
@@ -21,8 +21,13 @@ public enum Space: CGFloat {
/// 16
case small = 16
+ #if os(macOS)
+ /// 20
+ case medium = 20
+ #else
/// 24
case medium = 24
+ #endif
/// 32
case large = 32
diff --git a/Sources/OversizeUI/Core/Typography.swift b/Sources/OversizeUI/Core/Typography.swift
index 818cc89..12a9631 100644
--- a/Sources/OversizeUI/Core/Typography.swift
+++ b/Sources/OversizeUI/Core/Typography.swift
@@ -40,7 +40,7 @@ public struct Typography: ViewModifier {
content
.font(.system(fontStyle, design: fontDesign).weight(fontWeight).leading(.tight))
.frame(minHeight: lineHeight)
- // .lineSpacing(lineHeight)
+ .lineSpacing(lineHeight * 0.2)
}
private var lineHeight: CGFloat {
diff --git a/Sources/OversizeUI/Extensions/Color/Color+RawRepresentable.swift b/Sources/OversizeUI/Extensions/Color/Color+RawRepresentable.swift
index 8d2657e..3af7df7 100644
--- a/Sources/OversizeUI/Extensions/Color/Color+RawRepresentable.swift
+++ b/Sources/OversizeUI/Extensions/Color/Color+RawRepresentable.swift
@@ -8,7 +8,7 @@ import Foundation
import SwiftUI
import UIKit
-extension Color: RawRepresentable {
+extension Color: @retroactive RawRepresentable {
@available(macOS, unavailable)
@available(watchOS, unavailable)
@available(tvOS, unavailable)
diff --git a/Sources/OversizeUI/Extensions/Image/Image+Extensions.swift b/Sources/OversizeUI/Extensions/Image/Image+Extensions.swift
index 053df9c..f89ef12 100644
--- a/Sources/OversizeUI/Extensions/Image/Image+Extensions.swift
+++ b/Sources/OversizeUI/Extensions/Image/Image+Extensions.swift
@@ -16,6 +16,13 @@ public extension Image {
public extension Image {
func icon(_ color: Color = Color.onSurfaceHighEmphasis) -> some View {
renderingMode(.template)
+ #if os(macOS)
+ .resizable()
+ .frame(
+ width: IconSizes.medium.rawValue,
+ height: IconSizes.medium.rawValue
+ )
+ #endif
.foregroundColor(color)
}
}
@@ -24,7 +31,10 @@ public extension Image {
func icon(_ color: Color = Color.onSurfaceHighEmphasis, size: IconSizes) -> some View {
renderingMode(.template)
.resizable()
- .frame(width: size.rawValue, height: size.rawValue)
+ .frame(
+ width: size.rawValue,
+ height: size.rawValue
+ )
.foregroundColor(color)
}
}
diff --git a/Sources/OversizeUI/Extensions/Spacing/Spacing.swift b/Sources/OversizeUI/Extensions/Spacing/Spacing.swift
index 588a019..ea583fd 100644
--- a/Sources/OversizeUI/Extensions/Spacing/Spacing.swift
+++ b/Sources/OversizeUI/Extensions/Spacing/Spacing.swift
@@ -59,3 +59,15 @@ public extension EdgeInsets {
self = .init(top: top.rawValue, leading: leading.rawValue, bottom: bottom.rawValue, trailing: trailing.rawValue)
}
}
+
+public extension EdgeInsets {
+ init(_ all: Space) {
+ self = .init(top: all.rawValue, leading: all.rawValue, bottom: all.rawValue, trailing: all.rawValue)
+ }
+}
+
+public extension EdgeInsets {
+ init(horizontal: Space, vertical: Space) {
+ self = .init(top: vertical.rawValue, leading: horizontal.rawValue, bottom: vertical.rawValue, trailing: horizontal.rawValue)
+ }
+}
diff --git a/Sources/OversizeUI/Resources/Colors.xcassets/Background/BackgroundTertiary.colorset/Contents.json b/Sources/OversizeUI/Resources/Colors.xcassets/Background/BackgroundTertiary.colorset/Contents.json
index d2f2378..2924543 100644
--- a/Sources/OversizeUI/Resources/Colors.xcassets/Background/BackgroundTertiary.colorset/Contents.json
+++ b/Sources/OversizeUI/Resources/Colors.xcassets/Background/BackgroundTertiary.colorset/Contents.json
@@ -5,9 +5,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
- "blue" : "0.902",
- "green" : "0.902",
- "red" : "0.902"
+ "blue" : "0xE6",
+ "green" : "0xE6",
+ "red" : "0xE6"
}
},
"idiom" : "universal"
diff --git a/Sources/OversizeUI/Shapes/RoundedRectangleCorner.swift b/Sources/OversizeUI/Shapes/RoundedRectangleCorner.swift
index bb14bbe..4de13c5 100644
--- a/Sources/OversizeUI/Shapes/RoundedRectangleCorner.swift
+++ b/Sources/OversizeUI/Shapes/RoundedRectangleCorner.swift
@@ -3,28 +3,19 @@
// RoundedRectangleCorner.swift, created on 11.09.2021
//
-#if os(iOS)
+#if canImport(UIKit)
import SwiftUI
-@available(watchOS, unavailable)
-@available(tvOS, unavailable)
-@available(macOS, unavailable)
public struct RoundedRectangleCorner: Shape {
private var radius: CGFloat = .infinity
private var corners: UIRectCorner = .allCorners
- @available(watchOS, unavailable)
- @available(tvOS, unavailable)
- @available(macOS, unavailable)
public init(radius: CGFloat, corners: UIRectCorner) {
self.radius = radius
self.corners = corners
}
- @available(watchOS, unavailable)
- @available(tvOS, unavailable)
- @available(macOS, unavailable)
public init(radius: Radius, corners: UIRectCorner) {
self.radius = radius.rawValue
self.corners = corners