🏗️ Reorganize files
This commit is contained in:
@@ -269,11 +269,13 @@
|
|||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = AlloVoisinsSwiftUI/Info.plist;
|
INFOPLIST_FILE = AlloVoisinsSwiftUI/Info.plist;
|
||||||
|
INFOPLIST_KEY_NSCameraUsageDescription = "Nous utilisons la caméra afin de compléter la photo de profil";
|
||||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
INFOPLIST_KEY_UIUserInterfaceStyle = Light;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 16.6;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
@@ -299,11 +301,13 @@
|
|||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = AlloVoisinsSwiftUI/Info.plist;
|
INFOPLIST_FILE = AlloVoisinsSwiftUI/Info.plist;
|
||||||
|
INFOPLIST_KEY_NSCameraUsageDescription = "Nous utilisons la caméra afin de compléter la photo de profil";
|
||||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
INFOPLIST_KEY_UIUserInterfaceStyle = Light;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 16.6;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ struct AlloVoisinsSwiftUIApp: App {
|
|||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup {
|
WindowGroup {
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
ResiliationNavigationScreen(viewModel: ResiliationViewModel(resiliationType: .apProWithTrial))
|
MarketingSupportSelectionView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,16 +13,18 @@ struct SQButton: View {
|
|||||||
var textColor: Color = .black
|
var textColor: Color = .black
|
||||||
var textSize: CGFloat = 16
|
var textSize: CGFloat = 16
|
||||||
var font: SQTextFont = .demiBold
|
var font: SQTextFont = .demiBold
|
||||||
|
var icon: SQIcon?
|
||||||
var isFull: Bool = true
|
var isFull: Bool = true
|
||||||
@Binding var isLoading: Bool
|
@Binding var isLoading: Bool
|
||||||
let action: () -> Void
|
let action: () -> Void
|
||||||
|
|
||||||
init(_ title: String, color: Color = .sqNeutral(10), textColor: Color = .black, textSize: CGFloat = 16, font: SQTextFont = .demiBold, isFull: Bool = true, isLoading: Binding<Bool> = .constant(false), action: @escaping () -> Void) {
|
init(_ title: String, color: Color = .sqNeutral(10), textColor: Color = .black, textSize: CGFloat = 16, font: SQTextFont = .demiBold, icon: SQIcon? = nil, isFull: Bool = true, isLoading: Binding<Bool> = .constant(false), action: @escaping () -> Void) {
|
||||||
self.title = title
|
self.title = title
|
||||||
self.color = color
|
self.color = color
|
||||||
self.textColor = textColor
|
self.textColor = textColor
|
||||||
self.textSize = textSize
|
self.textSize = textSize
|
||||||
self.font = font
|
self.font = font
|
||||||
|
self.icon = icon
|
||||||
self.isFull = isFull
|
self.isFull = isFull
|
||||||
self._isLoading = isLoading
|
self._isLoading = isLoading
|
||||||
self.action = action
|
self.action = action
|
||||||
@@ -45,7 +47,13 @@ struct SQButton: View {
|
|||||||
.stroke(color, lineWidth: 1)
|
.stroke(color, lineWidth: 1)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
HStack {
|
||||||
|
if (icon != nil) {
|
||||||
|
icon
|
||||||
|
}
|
||||||
|
|
||||||
SQText(title, size: textSize, font: font, textColor: textColor)
|
SQText(title, size: textSize, font: font, textColor: textColor)
|
||||||
|
}
|
||||||
.padding(.horizontal, 30)
|
.padding(.horizontal, 30)
|
||||||
.padding(.vertical, 12)
|
.padding(.vertical, 12)
|
||||||
.frame(height: 40, alignment: .center)
|
.frame(height: 40, alignment: .center)
|
||||||
@@ -89,5 +97,7 @@ enum SQButtonStyle {
|
|||||||
.sqStyle()
|
.sqStyle()
|
||||||
SQButton("C'est parti !", color: .sqOrange(50), textColor: .white) {}
|
SQButton("C'est parti !", color: .sqOrange(50), textColor: .white) {}
|
||||||
.sqStyle()
|
.sqStyle()
|
||||||
|
SQButton("Imprimer", color: .sqNeutral(100), textColor: .white, icon: SQIcon(.print, color: .white)) {}
|
||||||
|
.sqStyle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
//
|
||||||
|
// SQChips.swift
|
||||||
|
// AlloVoisinsSwiftUI
|
||||||
|
//
|
||||||
|
// Created by Victor on 24/10/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct SQChipOption: Identifiable, Equatable {
|
||||||
|
var id: Int
|
||||||
|
let label: String
|
||||||
|
let bgColor: Color
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SQChips: View {
|
||||||
|
var options: [SQChipOption]
|
||||||
|
@Binding var selectedOption: SQChipOption
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
HStack {
|
||||||
|
ForEach(options) { option in
|
||||||
|
SQChip(option: option, selectedOption: $selectedOption)
|
||||||
|
.onTapGesture {
|
||||||
|
self.selectedOption = option
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate struct SQChip: View {
|
||||||
|
var option: SQChipOption
|
||||||
|
@Binding var selectedOption: SQChipOption
|
||||||
|
private var isSelected: Bool {
|
||||||
|
selectedOption.id == option.id
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
SQText(option.label, size: 14, font: .demiBold, textColor: isSelected ? .white : Color.sqNeutral(100))
|
||||||
|
.padding(.horizontal, 12)
|
||||||
|
.padding(.vertical, 8)
|
||||||
|
.background(isSelected ? option.bgColor : Color.clear)
|
||||||
|
.cornerRadius(8, corners: .allCorners)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 8)
|
||||||
|
.stroke(
|
||||||
|
isSelected && option.bgColor != .white ? option.bgColor : Color.sqNeutral(100), lineWidth: 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
SQChips(options: [
|
||||||
|
SQChipOption(id: 0, label: "Toutes", bgColor: .sqNeutral(100)),
|
||||||
|
SQChipOption(id: 1, label: "Non lues", bgColor: .sqSemanticBlue),
|
||||||
|
SQChipOption(id: 2, label: "Archivées", bgColor: .sqSemanticCritical)
|
||||||
|
], selectedOption: .constant(SQChipOption(id: 1, label: "Non lues", bgColor: .sqSemanticBlue)))
|
||||||
|
}
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
//
|
||||||
|
// SQColorPicker.swift
|
||||||
|
// AlloVoisinsSwiftUI
|
||||||
|
//
|
||||||
|
// Created by Victor on 13/11/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct SQColorPicker: View {
|
||||||
|
@Binding var selectedColor: Color
|
||||||
|
@State private var showColorPicker = false
|
||||||
|
@State private var isCustomColor = false
|
||||||
|
|
||||||
|
let colors: [Color] = [
|
||||||
|
.sqNeutral(),
|
||||||
|
.sqGreen(50),
|
||||||
|
.sqPink(),
|
||||||
|
.sqBlue(50),
|
||||||
|
.sqOrange(50),
|
||||||
|
.sqGrape(80),
|
||||||
|
.sqForest(80)
|
||||||
|
]
|
||||||
|
var title: String
|
||||||
|
var subtitle: String?
|
||||||
|
|
||||||
|
init(_ title: String, selectedColor: Binding<Color>, subtitle: String? = nil) {
|
||||||
|
self.title = title
|
||||||
|
self.subtitle = subtitle
|
||||||
|
self._selectedColor = selectedColor
|
||||||
|
let initialColor = selectedColor.wrappedValue
|
||||||
|
_isCustomColor = State(initialValue: !colors.contains(initialColor))
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
SQText(title, size: 18, font: .bold)
|
||||||
|
SQText(subtitle ?? "")
|
||||||
|
}
|
||||||
|
HStack {
|
||||||
|
ForEach(colors, id: \.self) { color in
|
||||||
|
colorDot(color)
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
|
||||||
|
SQImage("multicolor", height: 32)
|
||||||
|
.overlay {
|
||||||
|
if isCustomColor {
|
||||||
|
ZStack {
|
||||||
|
Circle()
|
||||||
|
.foregroundColor(selectedColor)
|
||||||
|
.frame(width: 20, height: 20)
|
||||||
|
Circle()
|
||||||
|
.inset(by: -2)
|
||||||
|
.strokeBorder(Color.white, lineWidth: 3)
|
||||||
|
.frame(width: 20, height: 20)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onTapGesture {
|
||||||
|
isCustomColor = true
|
||||||
|
showColorPicker.toggle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.sheet(isPresented: $showColorPicker) {
|
||||||
|
UIColorPickerViewController_SwiftUI(selectedColor: $selectedColor)
|
||||||
|
.background(Color.white)
|
||||||
|
.sqNavigationBar(title: "Choisir une couleur personnalisée")
|
||||||
|
SQButton("Valider", color: .sqNeutral(100), textColor: .white) {
|
||||||
|
showColorPicker.toggle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func colorDot(_ color: Color) -> some View {
|
||||||
|
HStack(alignment: .center, spacing: 8) {}
|
||||||
|
.padding(4)
|
||||||
|
.frame(width: 32, height: 32, alignment: .center)
|
||||||
|
.background(color)
|
||||||
|
.cornerRadius(32)
|
||||||
|
.overlay(
|
||||||
|
ZStack {
|
||||||
|
if selectedColor == color && !isCustomColor {
|
||||||
|
Circle()
|
||||||
|
.foregroundColor(color)
|
||||||
|
.frame(width: 20, height: 20)
|
||||||
|
Circle()
|
||||||
|
.inset(by: -2)
|
||||||
|
.strokeBorder(Color.white, lineWidth: 3)
|
||||||
|
.frame(width: 20, height: 20)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.onTapGesture {
|
||||||
|
selectedColor = color
|
||||||
|
isCustomColor = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UIColorPickerViewController_SwiftUI: UIViewControllerRepresentable {
|
||||||
|
@Binding var selectedColor: Color
|
||||||
|
|
||||||
|
func makeUIViewController(context: Context) -> UIColorPickerViewController {
|
||||||
|
let picker = UIColorPickerViewController()
|
||||||
|
picker.delegate = context.coordinator
|
||||||
|
picker.selectedColor = UIColor(selectedColor)
|
||||||
|
return picker
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUIViewController(_ uiViewController: UIColorPickerViewController, context: Context) {
|
||||||
|
uiViewController.selectedColor = UIColor(selectedColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeCoordinator() -> Coordinator {
|
||||||
|
Coordinator(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Coordinator: NSObject, UIColorPickerViewControllerDelegate {
|
||||||
|
var parent: UIColorPickerViewController_SwiftUI
|
||||||
|
|
||||||
|
init(_ parent: UIColorPickerViewController_SwiftUI) {
|
||||||
|
self.parent = parent
|
||||||
|
}
|
||||||
|
|
||||||
|
func colorPickerViewControllerDidSelectColor(_ viewController: UIColorPickerViewController) {
|
||||||
|
parent.selectedColor = Color(viewController.selectedColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
func colorPickerViewControllerDidFinish(_ viewController: UIColorPickerViewController) {
|
||||||
|
parent.selectedColor = Color(viewController.selectedColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preview
|
||||||
|
struct ColorPickerPreview: View {
|
||||||
|
@State private var selectedColor: Color = .sqNeutral()
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
SQColorPicker("Couleur principale", selectedColor: $selectedColor, subtitle: "Choisissez la couleur qui sera appliquée sur vos cartes de visite.")
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
ColorPickerPreview()
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
//
|
||||||
|
// SQFooter.swift
|
||||||
|
// AlloVoisinsSwiftUI
|
||||||
|
//
|
||||||
|
// Created by Victor on 13/11/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct SQFooter<Content: View>: View {
|
||||||
|
let content: Content
|
||||||
|
|
||||||
|
init(@ViewBuilder content: () -> Content) {
|
||||||
|
self.content = content()
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
HStack {
|
||||||
|
content
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
|
.background(Color.white)
|
||||||
|
.cornerRadius(8)
|
||||||
|
.shadow(color: Color(red: 0.09, green: 0.14, blue: 0.2).opacity(0.1), radius: 8, x: 0, y: -4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
SQFooter {
|
||||||
|
SQButton("test") {
|
||||||
|
|
||||||
|
}
|
||||||
|
SQButton("test") {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
//
|
||||||
|
// SQImage.swift
|
||||||
|
// AlloVoisinsSwiftUI
|
||||||
|
//
|
||||||
|
// Created by Victor on 13/11/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct SQImage: View {
|
||||||
|
var imageName: String
|
||||||
|
var height: CGFloat
|
||||||
|
|
||||||
|
init(_ imageName: String, height: CGFloat) {
|
||||||
|
self.imageName = imageName
|
||||||
|
self.height = height
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Image(imageName)
|
||||||
|
.resizable()
|
||||||
|
.scaledToFit()
|
||||||
|
.frame(height: height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
SQImage("flyers", height: 100)
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
//
|
||||||
|
// SQNavigationBar.swift
|
||||||
|
// AlloVoisinsSwiftUI
|
||||||
|
//
|
||||||
|
// Created by Victor on 14/10/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct SQNavigationBar: ViewModifier {
|
||||||
|
@Environment(\.dismiss) private var dismiss
|
||||||
|
let title: String
|
||||||
|
let style: SQNavigationBarStyle
|
||||||
|
let showBackButton: Bool
|
||||||
|
let backAction: (() -> Void)?
|
||||||
|
|
||||||
|
func body(content: Content) -> some View {
|
||||||
|
content
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItem(placement: .principal) {
|
||||||
|
SQText(title, size: 18, font: .bold)
|
||||||
|
.foregroundColor(style.foregroundColor)
|
||||||
|
}
|
||||||
|
if backAction != nil {
|
||||||
|
ToolbarItem(placement: .navigationBarLeading) {
|
||||||
|
if showBackButton {
|
||||||
|
Button(action: {
|
||||||
|
backAction?()
|
||||||
|
}) {
|
||||||
|
style.closeIcon
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if showBackButton {
|
||||||
|
ToolbarItem(placement: .navigationBarLeading) {
|
||||||
|
Button(action: {
|
||||||
|
dismiss()
|
||||||
|
}) {
|
||||||
|
style.closeIcon
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.toolbarBackground(style.backgroundColor, for: .navigationBar)
|
||||||
|
.toolbarBackground(.visible, for: .navigationBar)
|
||||||
|
.navigationBarBackButtonHidden()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension View {
|
||||||
|
func sqNavigationBar(title: String, style: SQNavigationBarStyle = .white, showBackButton: Bool = true, backAction: (() -> Void)? = nil) -> some View {
|
||||||
|
modifier(SQNavigationBar(title: title, style: style, showBackButton: showBackButton, backAction: backAction))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SQNavigationBarStyle {
|
||||||
|
case white
|
||||||
|
case booster
|
||||||
|
case boosterFreeTrialResiliation
|
||||||
|
|
||||||
|
var backgroundColor: Color {
|
||||||
|
switch self {
|
||||||
|
case .white: return .white
|
||||||
|
case .booster: return .sqRoyal(60)
|
||||||
|
case .boosterFreeTrialResiliation: return .sqRoyal(60)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var foregroundColor: Color {
|
||||||
|
switch self {
|
||||||
|
case .white: return .sqNeutral(100)
|
||||||
|
case .booster: return .white
|
||||||
|
case .boosterFreeTrialResiliation: return .white
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var closeIcon: SQIcon {
|
||||||
|
switch self {
|
||||||
|
case .white: return SQIcon(.chevron_left, size: .l, color: foregroundColor)
|
||||||
|
case .booster: return SQIcon(.xmark, size: .l, color: foregroundColor)
|
||||||
|
case .boosterFreeTrialResiliation: return SQIcon(.chevron_left, size: .l, color: foregroundColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
118
AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQPicker.swift
Normal file
118
AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQPicker.swift
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
//
|
||||||
|
// SQPicker.swift
|
||||||
|
// AlloVoisinsSwiftUI
|
||||||
|
//
|
||||||
|
// Created by Victor on 18/10/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct PickerOption: Identifiable, Hashable {
|
||||||
|
let id = UUID()
|
||||||
|
let text: String
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SQPicker: View {
|
||||||
|
@Binding var selection: PickerOption
|
||||||
|
let options: [PickerOption]
|
||||||
|
@State private var isPresenting = false
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
GeometryReader { geometry in
|
||||||
|
ZStack {
|
||||||
|
// Contenu visuel
|
||||||
|
HStack {
|
||||||
|
SQText(selection.text)
|
||||||
|
Spacer()
|
||||||
|
SQIcon(.chevron_down)
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
.frame(width: geometry.size.width)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 8)
|
||||||
|
.inset(by: 0.5)
|
||||||
|
.stroke(Color.sqNeutral(30), lineWidth: 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Menu invisible mais cliquable
|
||||||
|
MenuPicker(selection: $selection, options: options, isPresenting: $isPresenting)
|
||||||
|
.frame(width: geometry.size.width)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
|
.tint(.sqNeutral(100))
|
||||||
|
}
|
||||||
|
|
||||||
|
func triggerPickerMenu() {
|
||||||
|
isPresenting = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MenuPicker: UIViewRepresentable {
|
||||||
|
@Binding var selection: PickerOption
|
||||||
|
let options: [PickerOption]
|
||||||
|
@Binding var isPresenting: Bool
|
||||||
|
|
||||||
|
func makeUIView(context: Context) -> UIButton {
|
||||||
|
let button = UIButton(type: .custom)
|
||||||
|
button.setTitle("", for: .normal)
|
||||||
|
updateMenu(button)
|
||||||
|
return button
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUIView(_ uiView: UIButton, context: Context) {
|
||||||
|
updateMenu(uiView)
|
||||||
|
if isPresenting {
|
||||||
|
uiView.sendActions(for: .menuActionTriggered)
|
||||||
|
isPresenting = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateMenu(_ button: UIButton) {
|
||||||
|
let menu = UIMenu(title: "", children: options.map { option in
|
||||||
|
UIAction(title: option.text, state: option == selection ? .on : .off) { _ in
|
||||||
|
self.selection = option
|
||||||
|
}
|
||||||
|
}.reversed())
|
||||||
|
|
||||||
|
button.showsMenuAsPrimaryAction = true
|
||||||
|
button.menu = menu
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeCoordinator() -> Coordinator {
|
||||||
|
Coordinator(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Coordinator: NSObject {
|
||||||
|
var parent: MenuPicker
|
||||||
|
|
||||||
|
init(_ parent: MenuPicker) {
|
||||||
|
self.parent = parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SQPickerPreview: View {
|
||||||
|
@State private var selectedDuration: PickerOption
|
||||||
|
let durationOptions: [PickerOption]
|
||||||
|
|
||||||
|
init() {
|
||||||
|
let options = [
|
||||||
|
PickerOption(text: "1 mois"),
|
||||||
|
PickerOption(text: "2 mois"),
|
||||||
|
PickerOption(text: "3 mois")
|
||||||
|
]
|
||||||
|
_selectedDuration = State(initialValue: options[0])
|
||||||
|
durationOptions = options
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
SQPicker(selection: $selectedDuration, options: durationOptions)
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
SQPickerPreview()
|
||||||
|
}
|
||||||
@@ -34,6 +34,9 @@ struct SQRadio: View {
|
|||||||
Group {
|
Group {
|
||||||
if orientation == .horizontal {
|
if orientation == .horizontal {
|
||||||
HorizontalRadioButtons(options: options, selectedIndex: $selectedIndex)
|
HorizontalRadioButtons(options: options, selectedIndex: $selectedIndex)
|
||||||
|
.background(Color.blue)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
VStack(alignment: .leading, spacing: 16) {
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
radioButtons
|
radioButtons
|
||||||
@@ -63,7 +66,8 @@ private struct HorizontalRadioButtons: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
GeometryReader { geometry in
|
GeometryReader { geometry in
|
||||||
HStack(alignment: .top, spacing: 16) {
|
HStack(alignment: .top, spacing: 0) {
|
||||||
|
Spacer()
|
||||||
ForEach(Array(options.enumerated()), id: \.offset) { index, option in
|
ForEach(Array(options.enumerated()), id: \.offset) { index, option in
|
||||||
RadioButton(
|
RadioButton(
|
||||||
title: option,
|
title: option,
|
||||||
@@ -73,10 +77,12 @@ private struct HorizontalRadioButtons: View {
|
|||||||
),
|
),
|
||||||
orientation: .horizontal
|
orientation: .horizontal
|
||||||
)
|
)
|
||||||
.frame(width: (geometry.size.width - CGFloat(options.count - 1) * 16) / CGFloat(options.count))
|
.frame(width: geometry.size.width / CGFloat(options.count))
|
||||||
}
|
}
|
||||||
|
Spacer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.frame(height: 80)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,6 +131,6 @@ private struct RadioButton: View {
|
|||||||
#Preview {
|
#Preview {
|
||||||
VStack(spacing: 32) {
|
VStack(spacing: 32) {
|
||||||
SQRadio(title: "Vertical", options: ["Option 1", "Option 2", "Option 3"], selectedIndex: .constant(0))
|
SQRadio(title: "Vertical", options: ["Option 1", "Option 2", "Option 3"], selectedIndex: .constant(0))
|
||||||
SQRadio(title: "Horizontal", orientation: .horizontal, options: ["Option 1", "Option 2", "Option 3"], selectedIndex: .constant(2))
|
SQRadio(title: "Horizontal", orientation: .horizontal, options: ["1\n Non Jamais", "2", "3", "4", "5\nOui"], selectedIndex: .constant(2))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -44,6 +44,14 @@ extension Color {
|
|||||||
return Color("GRAPE_\(variant)")
|
return Color("GRAPE_\(variant)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func sqPink(_ variant: Int = 60) -> Color {
|
||||||
|
return Color("PINK_\(variant)")
|
||||||
|
}
|
||||||
|
|
||||||
|
static func sqForest(_ variant: Int = 60) -> Color {
|
||||||
|
return Color("FOREST_\(variant)")
|
||||||
|
}
|
||||||
|
|
||||||
static let sqSemanticRed = Color("SEMANTIC_RED")
|
static let sqSemanticRed = Color("SEMANTIC_RED")
|
||||||
static let sqSemanticOrange = Color("SEMANTIC_CRITICAL")
|
static let sqSemanticOrange = Color("SEMANTIC_CRITICAL")
|
||||||
static let sqSemanticGreen = Color("SEMANTIC_GREEN")
|
static let sqSemanticGreen = Color("SEMANTIC_GREEN")
|
||||||
@@ -52,4 +60,33 @@ extension Color {
|
|||||||
static let sqSemanticWarning = Color("SEMANTIC_WARNING")
|
static let sqSemanticWarning = Color("SEMANTIC_WARNING")
|
||||||
static let sqSemanticPositive = Color("SEMANTIC_GREEN")
|
static let sqSemanticPositive = Color("SEMANTIC_GREEN")
|
||||||
static let sqSemanticNegative = Color("SEMANTIC_RED")
|
static let sqSemanticNegative = Color("SEMANTIC_RED")
|
||||||
|
|
||||||
|
var isLight: Bool {
|
||||||
|
let components = UIColor(self).cgColor.components ?? [0, 0, 0, 0]
|
||||||
|
|
||||||
|
if components.count == 2 {
|
||||||
|
let gray = components[0]
|
||||||
|
let luminance = 0.299 * gray + 0.587 * gray + 0.114 * gray
|
||||||
|
return luminance > 0.5
|
||||||
|
}
|
||||||
|
|
||||||
|
if components.count >= 3 {
|
||||||
|
let red = components[0]
|
||||||
|
let green = components[1]
|
||||||
|
let blue = components[2]
|
||||||
|
|
||||||
|
let r = red * 255
|
||||||
|
let g = green * 255
|
||||||
|
let b = blue * 255
|
||||||
|
|
||||||
|
let luminance = 0.299 * r + 0.587 * g + 0.114 * b
|
||||||
|
return luminance > 128
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var appropriateTextColor: Color {
|
||||||
|
isLight ? .black : .white
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
//
|
||||||
|
// CardColorSelectionView.swift
|
||||||
|
// AlloVoisinsSwiftUI
|
||||||
|
//
|
||||||
|
// Created by Victor on 12/11/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct CardColorSelectionView: View {
|
||||||
|
@State var goToNext: Bool = false
|
||||||
|
@State private var applyToAll: Bool = true
|
||||||
|
@State private var selectedColor: Color = .sqNeutral()
|
||||||
|
var selectedTemplate: CardTemplate
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack {
|
||||||
|
VStack(alignment: .leading, spacing: 32) {
|
||||||
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
|
SQColorPicker("Couleur principale", selectedColor: $selectedColor, subtitle: "Choisissez la couleur qui sera appliquée sur vos cartes de visite.")
|
||||||
|
SQText("Aperçu", size: 18, font: .bold)
|
||||||
|
selectedTemplate.imageColorTemplate(isLight: selectedColor.isLight)
|
||||||
|
.background(selectedColor)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
}
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Toggle(isOn: $applyToAll) {
|
||||||
|
SQText("Appliquer cette couleur sur mes prospectus, mes devis et mes factures")
|
||||||
|
}
|
||||||
|
.tint(Color.sqNeutral(100))
|
||||||
|
SQText("Nous vous recommandons d’appliquer la même couleur sur tous vos documents.", size: 12)
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
SQFooter {
|
||||||
|
SQButton("Continuer", color: .sqNeutral(100), textColor: .white) {
|
||||||
|
self.goToNext.toggle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.sqNavigationBar(title: "Choix de la couleur ")
|
||||||
|
.navigationDestination(isPresented: $goToNext) {
|
||||||
|
CardFormView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
CardColorSelectionView(selectedTemplate: CardTemplate.template4)
|
||||||
|
}
|
||||||
164
AlloVoisinsSwiftUI/Features/Marketing/Views/CardFormView.swift
Normal file
164
AlloVoisinsSwiftUI/Features/Marketing/Views/CardFormView.swift
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
//
|
||||||
|
// CardFormView.swift
|
||||||
|
// AlloVoisinsSwiftUI
|
||||||
|
//
|
||||||
|
// Created by Victor on 12/11/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
import PhotosUI
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct CardFormView: View {
|
||||||
|
@State var title: String = ""
|
||||||
|
@State var subtitle: String = ""
|
||||||
|
@State var job: String = ""
|
||||||
|
@State var showRating: Bool = true
|
||||||
|
@State var phoneNumber: String = ""
|
||||||
|
@State var address: String = ""
|
||||||
|
@State var selectedPicture: PhotosPickerItem?
|
||||||
|
@State private var image: Image?
|
||||||
|
@State private var showImagePicker = false
|
||||||
|
@State private var showPhotoPicker = false
|
||||||
|
@State private var showActionSheet = false
|
||||||
|
@State private var useCamera = false
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack {
|
||||||
|
ScrollView {
|
||||||
|
VStack {
|
||||||
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
SQText("Informations", size: 24, font: .bold)
|
||||||
|
SQText("Les modifications apportées sur votre carte de visite ne seront pas reportées sur votre profil.")
|
||||||
|
}
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
SQText("Image", font: .demiBold)
|
||||||
|
|
||||||
|
ZStack(alignment: .bottomTrailing) {
|
||||||
|
Button(action: { showActionSheet = true }) {
|
||||||
|
if let image = image {
|
||||||
|
image
|
||||||
|
.resizable()
|
||||||
|
.scaledToFill()
|
||||||
|
.frame(width: 160, height: 160)
|
||||||
|
.clipShape(Circle())
|
||||||
|
} else {
|
||||||
|
SQIcon(.camera, customSize: 40)
|
||||||
|
.frame(width: 160, height: 160)
|
||||||
|
.background(Color.sqNeutral(10))
|
||||||
|
.clipShape(Circle())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if image != nil {
|
||||||
|
SQIcon(.pen_to_square, size: .m)
|
||||||
|
.padding(8)
|
||||||
|
.background(Circle().fill(Color.sqNeutral(15)))
|
||||||
|
.shadow(color: .sqNeutral(100).opacity(0.1), radius: 4, x: 0, y: 2)
|
||||||
|
.offset(x: -8, y: -8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
}
|
||||||
|
SQTextField("Titre", placeholder: "Ex : Pro Solutions", errorText: "", text: $title)
|
||||||
|
SQTextField("Sous-titre", placeholder: "Ex : Martin Dupont", errorText: "", text: $subtitle, isOptional: true)
|
||||||
|
SQTextField("Métier", placeholder: "Ex : Dépannage électroménager", errorText: "", text: $job, isOptional: true)
|
||||||
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
|
Toggle(isOn: $showRating) {
|
||||||
|
SQText("Afficher ma note AlloVoisins", font: .demiBold)
|
||||||
|
}
|
||||||
|
.tint(Color.sqNeutral(100))
|
||||||
|
HStack(spacing: 4) {
|
||||||
|
SQIcon(.star, type: .solid, color: .sqGold(50))
|
||||||
|
SQText("4,7/5 sur", size: 12, font: .demiBold)
|
||||||
|
SQImage("logo", height: 14)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 2)
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
|
||||||
|
Rectangle()
|
||||||
|
.frame(height: 16)
|
||||||
|
.foregroundColor(Color.sqNeutral(10))
|
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
|
SQTextField("Numéro de téléphone", placeholder: "Ex : 06 12 34 56 78", errorText: "", text: $phoneNumber)
|
||||||
|
SQTextField("Adresse complète", placeholder: "Ex : 1 rue de la gare, 67000 Strasbourg", errorText: "", text: $address)
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SQFooter {
|
||||||
|
SQButton("Aperçu", color: .sqNeutral(100), textColor: .sqNeutral(100), icon: SQIcon(.eye, color: .sqNeutral(100)), isFull: false) {}
|
||||||
|
.sqStyle()
|
||||||
|
SQButton("Imprimer", color: .sqNeutral(100), textColor: .white, icon: SQIcon(.print, color: .white)) {}
|
||||||
|
.sqStyle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.confirmationDialog("Choisir une image", isPresented: $showActionSheet, actions: {
|
||||||
|
Button("Appareil photo") {
|
||||||
|
useCamera = true
|
||||||
|
showImagePicker = true
|
||||||
|
}
|
||||||
|
|
||||||
|
Button("Galerie") {
|
||||||
|
useCamera = false
|
||||||
|
showPhotoPicker = true
|
||||||
|
}
|
||||||
|
|
||||||
|
Button("Annuler", role: .cancel) {}
|
||||||
|
})
|
||||||
|
.photosPicker(isPresented: $showPhotoPicker, selection: $selectedPicture)
|
||||||
|
.sheet(isPresented: $showImagePicker) {
|
||||||
|
if useCamera {
|
||||||
|
ImagePicker(image: $image, sourceType: .camera)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.task(id: selectedPicture) {
|
||||||
|
if let data = try? await selectedPicture?.loadTransferable(type: Image.self) {
|
||||||
|
image = data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.sqNavigationBar(title: "Ma carte de visite")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ImagePicker: UIViewControllerRepresentable {
|
||||||
|
@Binding var image: Image?
|
||||||
|
let sourceType: UIImagePickerController.SourceType
|
||||||
|
|
||||||
|
func makeUIViewController(context: Context) -> UIImagePickerController {
|
||||||
|
let picker = UIImagePickerController()
|
||||||
|
picker.sourceType = sourceType
|
||||||
|
picker.delegate = context.coordinator
|
||||||
|
return picker
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}
|
||||||
|
|
||||||
|
func makeCoordinator() -> Coordinator {
|
||||||
|
Coordinator(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
|
||||||
|
let parent: ImagePicker
|
||||||
|
|
||||||
|
init(_ parent: ImagePicker) {
|
||||||
|
self.parent = parent
|
||||||
|
}
|
||||||
|
|
||||||
|
func imagePickerController(_ picker: UIImagePickerController,
|
||||||
|
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any])
|
||||||
|
{
|
||||||
|
if let uiImage = info[.originalImage] as? UIImage {
|
||||||
|
parent.image = Image(uiImage: uiImage)
|
||||||
|
}
|
||||||
|
picker.dismiss(animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
CardFormView()
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
//
|
||||||
|
// CardPrintView.swift
|
||||||
|
// AlloVoisinsSwiftUI
|
||||||
|
//
|
||||||
|
// Created by Victor on 13/11/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct CardPrintView: View {
|
||||||
|
var body: some View {
|
||||||
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
|
SQText("Génération d’une planche au format A4 comprenant 8 cartes de visite.", font: .demiBold)
|
||||||
|
Image("visit_card_print_template")
|
||||||
|
.resizable()
|
||||||
|
.scaledToFit()
|
||||||
|
.frame(height: 100)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
SQText("Nous vous recommandons : \n • Une impression haute qualité, en couleur \n • L’utilisation d’un papier d’au moins
200 g/m2 ")
|
||||||
|
SQButton("Imprimer", color: .sqNeutral(100), textColor: .white) {}
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
CardPrintView()
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
//
|
||||||
|
// CardTemplateSelectionView.swift
|
||||||
|
// AlloVoisinsSwiftUI
|
||||||
|
//
|
||||||
|
// Created by Victor on 12/11/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
enum CardTemplate: String, CaseIterable {
|
||||||
|
case template1
|
||||||
|
case template2
|
||||||
|
case template3
|
||||||
|
case template4
|
||||||
|
|
||||||
|
var image: SQImage {
|
||||||
|
switch self {
|
||||||
|
case .template1:
|
||||||
|
SQImage("visit_card_template_1", height: 222)
|
||||||
|
case .template2:
|
||||||
|
SQImage("visit_card_template_2", height: 222)
|
||||||
|
case .template3:
|
||||||
|
SQImage("visit_card_template_3", height: 222)
|
||||||
|
case .template4:
|
||||||
|
SQImage("visit_card_template_4", height: 222)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func imageColorTemplate(isLight: Bool) -> SQImage {
|
||||||
|
switch self {
|
||||||
|
case .template1:
|
||||||
|
return SQImage("visit_card_color_template_1", height: 222)
|
||||||
|
case .template2:
|
||||||
|
return SQImage("visit_card_color_template_2", height: 222)
|
||||||
|
case .template3:
|
||||||
|
return SQImage("visit_card_color_template_3", height: 222)
|
||||||
|
case .template4:
|
||||||
|
if isLight {
|
||||||
|
return SQImage("visit_card_color_template_4_black", height: 222)
|
||||||
|
} else {
|
||||||
|
return SQImage("visit_card_color_template_4_white", height: 222)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CardTemplateSelectionView: View {
|
||||||
|
@State var goToNext: Bool = false
|
||||||
|
@State var selectedTemplate: CardTemplate?
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack {
|
||||||
|
ScrollView {
|
||||||
|
LazyVStack(spacing: 16) {
|
||||||
|
ForEach(CardTemplate.allCases, id: \.self) { template in
|
||||||
|
template.image
|
||||||
|
.overlay(
|
||||||
|
Rectangle()
|
||||||
|
.inset(by: selectedTemplate == template ? 1 : 0.5)
|
||||||
|
.stroke(selectedTemplate == template ? Color.sqNeutral(100) : Color.sqNeutral(20), lineWidth: selectedTemplate == template ? 2 : 1)
|
||||||
|
)
|
||||||
|
.opacity(selectedTemplate != nil ? selectedTemplate == template ? 1 : 0.5 : 1)
|
||||||
|
.onTapGesture {
|
||||||
|
selectedTemplate = template
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SQFooter {
|
||||||
|
SQButton("Continuer", color: .sqNeutral(100), textColor: .white) {
|
||||||
|
if selectedTemplate != nil {
|
||||||
|
goToNext.toggle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.disabled(selectedTemplate == nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.sqNavigationBar(title: "Choix du modèle")
|
||||||
|
.navigationDestination(isPresented: $goToNext) {
|
||||||
|
CardColorSelectionView(selectedTemplate: selectedTemplate ?? CardTemplate.template1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
CardTemplateSelectionView()
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
//
|
||||||
|
// MarketingSupportSelectionView.swift
|
||||||
|
// AlloVoisinsSwiftUI
|
||||||
|
//
|
||||||
|
// Created by Victor on 12/11/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct MarketingSupportSelectionView: View {
|
||||||
|
@State var goToNext: Bool = false
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ScrollView {
|
||||||
|
Spacer()
|
||||||
|
.frame(height: 16)
|
||||||
|
// Visit cards
|
||||||
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
|
SQImage("visit_card_new", height: 200)
|
||||||
|
.cornerRadius(8, corners: [.topLeft, .topRight])
|
||||||
|
SQText("Mes cartes de visite", size: 18, font: .bold)
|
||||||
|
.padding(16)
|
||||||
|
}
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 8)
|
||||||
|
.inset(by: 0.5)
|
||||||
|
.stroke(Color.sqNeutral(20), lineWidth: 1)
|
||||||
|
)
|
||||||
|
.onTapGesture {
|
||||||
|
self.goToNext.toggle()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flyers
|
||||||
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
|
SQImage("flyers", height: 200)
|
||||||
|
.cornerRadius(8, corners: [.topLeft, .topRight])
|
||||||
|
SQText("Mes prospetus", size: 18, font: .bold)
|
||||||
|
.padding(16)
|
||||||
|
}
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 8)
|
||||||
|
.inset(by: 0.5)
|
||||||
|
.stroke(Color.sqNeutral(20), lineWidth: 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Quotes
|
||||||
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
|
SQImage("quotes", height: 200)
|
||||||
|
.cornerRadius(8, corners: [.topLeft, .topRight])
|
||||||
|
SQText("Mes prospetus", size: 18, font: .bold)
|
||||||
|
.padding(16)
|
||||||
|
}
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 8)
|
||||||
|
.inset(by: 0.5)
|
||||||
|
.stroke(Color.sqNeutral(20), lineWidth: 1)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.scrollIndicators(.hidden)
|
||||||
|
.sqNavigationBar(title: "Mes supports de communication")
|
||||||
|
.navigationDestination(isPresented: $goToNext) {
|
||||||
|
CardTemplateSelectionView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
MarketingSupportSelectionView()
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
//
|
||||||
|
// MessageCenterChipFiltersCellView.swift
|
||||||
|
// AlloVoisinsSwiftUI
|
||||||
|
//
|
||||||
|
// Created by Victor on 24/10/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
enum MessageCenterFilter: CaseIterable, Equatable {
|
||||||
|
case all
|
||||||
|
case unread
|
||||||
|
case archived
|
||||||
|
|
||||||
|
var chipOption: SQChipOption {
|
||||||
|
switch self {
|
||||||
|
case .all:
|
||||||
|
return SQChipOption(id: 0, label: "Toutes", bgColor: Color.sqNeutral(100))
|
||||||
|
case .unread:
|
||||||
|
return SQChipOption(id: 1, label: "Non lues", bgColor: Color.sqSemanticBlue)
|
||||||
|
case .archived:
|
||||||
|
return SQChipOption(id: 2, label: "Archivées", bgColor: Color.sqSemanticCritical)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MessageCenterChipFiltersCellView: View {
|
||||||
|
@State var selectedOption: SQChipOption
|
||||||
|
let options: [SQChipOption]
|
||||||
|
let onFilterSelected: (MessageCenterFilter) -> Void
|
||||||
|
|
||||||
|
init(onFilterSelected: @escaping (MessageCenterFilter) -> Void) {
|
||||||
|
self.options = [
|
||||||
|
MessageCenterFilter.all.chipOption,
|
||||||
|
MessageCenterFilter.unread.chipOption,
|
||||||
|
MessageCenterFilter.archived.chipOption
|
||||||
|
]
|
||||||
|
self._selectedOption = State(initialValue: MessageCenterFilter.all.chipOption)
|
||||||
|
self.onFilterSelected = onFilterSelected
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack {
|
||||||
|
SQChips(options: options, selectedOption: $selectedOption)
|
||||||
|
.onChange(of: selectedOption) { newValue in
|
||||||
|
if let filter = MessageCenterFilter.allCases.first(where: { $0.chipOption.id == newValue.id }) {
|
||||||
|
onFilterSelected(filter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
MessageCenterChipFiltersCellView { _ in
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0xFC",
|
||||||
|
"green" : "0xFF",
|
||||||
|
"red" : "0xEB"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0x40",
|
||||||
|
"green" : "0x4D",
|
||||||
|
"red" : "0x00"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0xF7",
|
||||||
|
"green" : "0xFD",
|
||||||
|
"red" : "0xD6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0xF1",
|
||||||
|
"green" : "0xFB",
|
||||||
|
"red" : "0xC1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0xE4",
|
||||||
|
"green" : "0xF4",
|
||||||
|
"red" : "0x96"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0xD3",
|
||||||
|
"green" : "0xE8",
|
||||||
|
"red" : "0x6C"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0xBE",
|
||||||
|
"green" : "0xD6",
|
||||||
|
"red" : "0x43"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0xA3",
|
||||||
|
"green" : "0xBE",
|
||||||
|
"red" : "0x1E"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0x83",
|
||||||
|
"green" : "0x9E",
|
||||||
|
"red" : "0x01"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0x63",
|
||||||
|
"green" : "0x77",
|
||||||
|
"red" : "0x00"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0xF1",
|
||||||
|
"green" : "0xEB",
|
||||||
|
"red" : "0xFF"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0x16",
|
||||||
|
"green" : "0x00",
|
||||||
|
"red" : "0x4D"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.894",
|
||||||
|
"green" : "0.851",
|
||||||
|
"red" : "1.000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0xD6",
|
||||||
|
"green" : "0xC6",
|
||||||
|
"red" : "0xFF"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0xB6",
|
||||||
|
"green" : "0x99",
|
||||||
|
"red" : "0xFF"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0x95",
|
||||||
|
"green" : "0x6B",
|
||||||
|
"red" : "0xFF"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.455",
|
||||||
|
"green" : "0.235",
|
||||||
|
"red" : "1.000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0x4F",
|
||||||
|
"green" : "0x16",
|
||||||
|
"red" : "0xDC"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.200",
|
||||||
|
"green" : "0.000",
|
||||||
|
"red" : "0.698"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0x25",
|
||||||
|
"green" : "0x00",
|
||||||
|
"red" : "0x80"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
12
AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/flyers.imageset/Contents.json
vendored
Normal file
12
AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/flyers.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "image (1).png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/flyers.imageset/image (1).png
vendored
Normal file
BIN
AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/flyers.imageset/image (1).png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 242 KiB |
12
AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/logo.imageset/Contents.json
vendored
Normal file
12
AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/logo.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "Logo.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/logo.imageset/Logo.png
vendored
Normal file
BIN
AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/logo.imageset/Logo.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.4 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user