Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ DerivedData/
.swiftpm/
*Generated/
Package.resolved

.home/

# SwiftLint Remote Config Cache
.swiftlint/RemoteConfigCache
1 change: 1 addition & 0 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ parent_config: https://raw.githubusercontent.com/leboncoin/spark-ios-common/main

excluded:
- "**/*.generated.swift"
- ".build/"
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
// TextLinkAccessibilityIdentifier.swift
// SparkComponentTextLink
//
// Created by robin.lemaire on 05/12/2023.
// Copyright © 2023 Leboncoin. All rights reserved.
// Created by robin.lemaire on 25/11/2025.
// Copyright © 2025 Leboncoin. All rights reserved.
//

/// The accessibility identifiers for the textLink.
Expand Down
26 changes: 22 additions & 4 deletions Sources/Core/Documentation.docc/Documentation.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# ``SparkComponentTextLink``

The Spark Textlink is a reference to a resource.
A textlink is a reference to a resource.


It can be external (e.g. a different web page) or internal (e.g. a specific element in the current page).

## Overview

Expand All @@ -10,12 +13,27 @@ It can be external (e.g. a different web page) or internal (e.g. a specific elem

### Implementation

- On SwiftUI, you need to use the ``TextLinkView`` View.
- On UIKit, you need to use the ``TextLinkUIView`` which inherit from an UIControl.
- On SwiftUI, you need to use the ``SparkTextLink`` View.
- On UIKit, you need to use the ``SparkUITextLink`` which inherit from an UIControl.

### Accessibility

By default, the accessibilityLabel is equals to the text.

To override this value, you need to set a new **accessibilityLabel**.


The image is not accessible.

### Rendering
- With image :
![TextLink rendering.](textlink_with_image.png)

- Without image :
![TextLink rendering.](textlink_without_image.png)

![Component rendering.](component.png)
- With a long text :
![TextLink rendering.](textlink_with_long_text.png)

### Resources

Expand Down
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
58 changes: 0 additions & 58 deletions Sources/Core/Enum/Public/TextLinkIntent.swift

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
// TextLinkAlignment.swift
// SparkComponentTextLink
//
// Created by robin.lemaire on 08/12/2023.
// Copyright © 2023 Leboncoin. All rights reserved.
// Created by robin.lemaire on 25/11/2025.
// Copyright © 2025 Leboncoin. All rights reserved.
//

/// The alignment of the switch.
Expand All @@ -19,6 +19,9 @@ public enum TextLinkAlignment: CaseIterable {

// MARK: - Properties

/// The default case. Equals to **.leadingImage**.
public static let `default`: Self = .leadingImage

var isTrailingImage: Bool {
return self == .trailingImage
}
Expand Down
86 changes: 86 additions & 0 deletions Sources/Core/Enum/TextLinkIntent.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//
// TextLinkIntent.swift
// SparkComponentTextLink
//
// Created by robin.lemaire on 25/11/2025.
// Copyright © 2025 Leboncoin. All rights reserved.
//

import SparkTheming

/// The intent of the text link.
public enum TextLinkIntent: Equatable {
case accent
case alert
case basic
case danger
case info
case main
case neutral
case success
case support

/// Use a custom color token. **Use it carefully**.
case custom(_ colorToken: any ColorToken)

@available(*, deprecated, message: "Use .custom instead.")
case accentContainer
@available(*, deprecated, message: "Use .custom instead.")
case onAccentContainer
@available(*, deprecated, message: "Use .custom instead.")
case alertContainer
@available(*, deprecated, message: "Use .custom instead.")
case basicContainer
@available(*, deprecated, message: "Use .custom instead.")
case dangerContainer
@available(*, deprecated, message: "Use .custom instead.")
case infoContainer
@available(*, deprecated, message: "Use .custom instead.")
case mainContainer
@available(*, deprecated, message: "Use .custom instead.")
case neutralContainer
@available(*, deprecated, message: "Use surface instead.")
case surface
@available(*, deprecated, message: "Use surface instead.")
case onSurface
@available(*, deprecated, message: "Use .custom instead.")
case successContainer
@available(*, deprecated, message: "Use .custom instead.")
case supportContainer

// MARK: - Properties

/// The default case. Equals to **.basic**.
public static let `default`: Self = .basic

// MARK: - Equatable

public static func == (lhs: TextLinkIntent, rhs: TextLinkIntent) -> Bool {
return switch (lhs, rhs) {
case (.accent, .accent): true
case (.alert, .alert): true
case (.basic, .basic): true
case (.danger, .danger): true
case (.info, .info): true
case (.main, .main): true
case (.neutral, .neutral): true
case (.success, .success): true
case (.support, .support): true
case (.accentContainer, .accentContainer): true
case (.onAccentContainer, .onAccentContainer): true
case (.alertContainer, .alertContainer): true
case (.basicContainer, .basicContainer): true
case (.dangerContainer, .dangerContainer): true
case (.infoContainer, .infoContainer): true
case (.mainContainer, .mainContainer): true
case (.neutralContainer, .neutralContainer): true
case (.surface, .surface): true
case (.onSurface, .onSurface): true
case (.successContainer, .successContainer): true
case (.supportContainer, .supportContainer): true
case (.custom(let lhsValue), .custom(let rhsValue)):
lhsValue.equals(rhsValue)
default: false
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
// TextLinkTypography.swift
// SparkComponentTextLink
//
// Created by robin.lemaire on 06/12/2023.
// Copyright © 2023 Leboncoin. All rights reserved.
// Created by robin.lemaire on 25/11/2025.
// Copyright © 2025 Leboncoin. All rights reserved.
//

/// The typography of the text link.
Expand Down Expand Up @@ -36,4 +36,9 @@ public enum TextLinkTypography: CaseIterable {

/// Use the **callout** typography
case callout

// MARK: - Properties

/// The default case. Equals to **.body1**.
public static let `default`: Self = .body1
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
// TextLinkVariant.swift
// SparkComponentTextLink
//
// Created by robin.lemaire on 05/12/2023.
// Copyright © 2023 Leboncoin. All rights reserved.
// Created by robin.lemaire on 25/11/2025.
// Copyright © 2025 Leboncoin. All rights reserved.
//

import UIKit
Expand All @@ -16,4 +16,9 @@ public enum TextLinkVariant: CaseIterable {
/// A text link without any variant (underline).
/// *Not recommended, please use it carefully.*
case none

// MARK: - Properties

/// The default case. Equals to **.underline**.
public static let `default`: Self = .underline
}
23 changes: 23 additions & 0 deletions Sources/Core/Environment/TextLinkAlignmentEnvironmentValues.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// TextLinkAlignmentEnvironmentValues.swift
// SparkComponentTextLink
//
// Created by robin.lemaire on 29/10/2025.
// Copyright © 2025 Leboncoin. All rights reserved.
//

import SwiftUI

extension EnvironmentValues {
@Entry var textLinkAlignment: TextLinkAlignment = .default
}

public extension View {

/// Set the **alignment** on the ``SparkTextLink``.
///
/// The default value for this property is *TextLinkAlignment.default*.
func sparkTextLinkAlignment(_ alignment: TextLinkAlignment) -> some View {
self.environment(\.textLinkAlignment, alignment)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// TextLinkHighlightRangeEnvironmentValues.swift
// SparkComponentTextLink
//
// Created by robin.lemaire on 29/10/2025.
// Copyright © 2025 Leboncoin. All rights reserved.
//

import SwiftUI

struct TextLinkRange: Equatable {
var value: NSRange?
}

extension EnvironmentValues {
@Entry var textLinkHighlightRange: TextLinkRange = .init()
}

public extension View {

/// Set the **highlight range** on the ``SparkTextLink``.
///
/// The default value for this property is *nil*.
func sparkTextLinkHighlightRange(_ range: NSRange?) -> some View {
self.environment(\.textLinkHighlightRange, .init(value: range))
}
}
23 changes: 23 additions & 0 deletions Sources/Core/Environment/TextLinkIntentEnvironmentValues.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// TextLinkIntentEnvironmentValues.swift
// SparkComponentTextLink
//
// Created by robin.lemaire on 29/10/2025.
// Copyright © 2025 Leboncoin. All rights reserved.
//

import SwiftUI

extension EnvironmentValues {
@Entry var textLinkIntent: TextLinkIntent = .default
}

public extension View {

/// Set the **intent** on the ``SparkTextLink``.
///
/// The default value for this property is *TextLinkIntent.default*.
func sparkTextLinkIntent(_ intent: TextLinkIntent) -> some View {
self.environment(\.textLinkIntent, intent)
}
}
23 changes: 23 additions & 0 deletions Sources/Core/Environment/TextLinkTypographyEnvironmentValues.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// TextLinkTypographyEnvironmentValues.swift
// SparkComponentTextLink
//
// Created by robin.lemaire on 29/10/2025.
// Copyright © 2025 Leboncoin. All rights reserved.
//

import SwiftUI

extension EnvironmentValues {
@Entry var textLinkTypography: TextLinkTypography = .default
}

public extension View {

/// Set the **typography** on the ``SparkTextLink``.
///
/// The default value for this property is *TextLinkTypography.default*.
func sparkTextLinkTypography(_ typography: TextLinkTypography) -> some View {
self.environment(\.textLinkTypography, typography)
}
}
Loading