Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
cfa1e17b3e
commit
79d69a5495
@ -7,6 +7,7 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
EA69DC822F3C199C00592220 /* Bedrock in Frameworks */ = {isa = PBXBuildFile; productRef = EA69DC812F3C199C00592220 /* Bedrock */; };
|
||||||
EA837E672F107D6800077F87 /* Bedrock in Frameworks */ = {isa = PBXBuildFile; productRef = EA837E662F107D6800077F87 /* Bedrock */; };
|
EA837E672F107D6800077F87 /* Bedrock in Frameworks */ = {isa = PBXBuildFile; productRef = EA837E662F107D6800077F87 /* Bedrock */; };
|
||||||
EAAE892A2F12DE110075BC8A /* BusinessCardWatch Watch App.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = EA837F982F11B16400077F87 /* BusinessCardWatch Watch App.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
EAAE892A2F12DE110075BC8A /* BusinessCardWatch Watch App.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = EA837F982F11B16400077F87 /* BusinessCardWatch Watch App.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
EACLIP0012F200000000001 /* BusinessCardClip.app in Embed App Clips */ = {isa = PBXBuildFile; fileRef = EACLIP0012F200000000002 /* BusinessCardClip.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
EACLIP0012F200000000001 /* BusinessCardClip.app in Embed App Clips */ = {isa = PBXBuildFile; fileRef = EACLIP0012F200000000002 /* BusinessCardClip.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
@ -135,6 +136,7 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
EA837E672F107D6800077F87 /* Bedrock in Frameworks */,
|
EA837E672F107D6800077F87 /* Bedrock in Frameworks */,
|
||||||
|
EA69DC822F3C199C00592220 /* Bedrock in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -236,6 +238,7 @@
|
|||||||
name = BusinessCard;
|
name = BusinessCard;
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
EA837E662F107D6800077F87 /* Bedrock */,
|
EA837E662F107D6800077F87 /* Bedrock */,
|
||||||
|
EA69DC812F3C199C00592220 /* Bedrock */,
|
||||||
);
|
);
|
||||||
productName = BusinessCard;
|
productName = BusinessCard;
|
||||||
productReference = EA8379232F105F2600077F87 /* BusinessCard.app */;
|
productReference = EA8379232F105F2600077F87 /* BusinessCard.app */;
|
||||||
@ -372,7 +375,7 @@
|
|||||||
mainGroup = EA83791A2F105F2600077F87;
|
mainGroup = EA83791A2F105F2600077F87;
|
||||||
minimizedProjectReferenceProxies = 1;
|
minimizedProjectReferenceProxies = 1;
|
||||||
packageReferences = (
|
packageReferences = (
|
||||||
EA837E652F107D6800077F87 /* XCLocalSwiftPackageReference "../Frameworks/Bedrock" */,
|
EA69DC802F3C199C00592220 /* XCLocalSwiftPackageReference "../Bedrock" */,
|
||||||
);
|
);
|
||||||
preferredProjectObjectVersion = 77;
|
preferredProjectObjectVersion = 77;
|
||||||
productRefGroup = EA8379242F105F2600077F87 /* Products */;
|
productRefGroup = EA8379242F105F2600077F87 /* Products */;
|
||||||
@ -524,7 +527,7 @@
|
|||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)";
|
DEVELOPMENT_TEAM = 6R7KLBPBLZ;
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
ENABLE_TESTABILITY = YES;
|
ENABLE_TESTABILITY = YES;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
@ -589,7 +592,7 @@
|
|||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)";
|
DEVELOPMENT_TEAM = 6R7KLBPBLZ;
|
||||||
ENABLE_NS_ASSERTIONS = NO;
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
@ -628,8 +631,8 @@
|
|||||||
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "BusinessCard uses your photo library to add a profile photo to your business card.";
|
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "BusinessCard uses your photo library to add a profile photo to your business card.";
|
||||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
|
INFOPLIST_KEY_UILaunchScreen_BackgroundColor = LaunchBackground;
|
||||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
"INFOPLIST_KEY_UILaunchScreen_BackgroundColor" = LaunchBackground;
|
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
@ -665,8 +668,8 @@
|
|||||||
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "BusinessCard uses your photo library to add a profile photo to your business card.";
|
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "BusinessCard uses your photo library to add a profile photo to your business card.";
|
||||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
|
INFOPLIST_KEY_UILaunchScreen_BackgroundColor = LaunchBackground;
|
||||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
"INFOPLIST_KEY_UILaunchScreen_BackgroundColor" = LaunchBackground;
|
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
@ -852,8 +855,8 @@
|
|||||||
INFOPLIST_KEY_CFBundleDisplayName = BusinessCard;
|
INFOPLIST_KEY_CFBundleDisplayName = BusinessCard;
|
||||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
|
INFOPLIST_KEY_UILaunchScreen_BackgroundColor = LaunchBackground;
|
||||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
"INFOPLIST_KEY_UILaunchScreen_BackgroundColor" = LaunchBackground;
|
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 26.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 26.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
@ -890,8 +893,8 @@
|
|||||||
INFOPLIST_KEY_CFBundleDisplayName = BusinessCard;
|
INFOPLIST_KEY_CFBundleDisplayName = BusinessCard;
|
||||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
|
INFOPLIST_KEY_UILaunchScreen_BackgroundColor = LaunchBackground;
|
||||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
"INFOPLIST_KEY_UILaunchScreen_BackgroundColor" = LaunchBackground;
|
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 26.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 26.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
@ -973,13 +976,17 @@
|
|||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
/* Begin XCLocalSwiftPackageReference section */
|
/* Begin XCLocalSwiftPackageReference section */
|
||||||
EA837E652F107D6800077F87 /* XCLocalSwiftPackageReference "../Frameworks/Bedrock" */ = {
|
EA69DC802F3C199C00592220 /* XCLocalSwiftPackageReference "../Bedrock" */ = {
|
||||||
isa = XCLocalSwiftPackageReference;
|
isa = XCLocalSwiftPackageReference;
|
||||||
relativePath = ../Frameworks/Bedrock;
|
relativePath = ../Bedrock;
|
||||||
};
|
};
|
||||||
/* End XCLocalSwiftPackageReference section */
|
/* End XCLocalSwiftPackageReference section */
|
||||||
|
|
||||||
/* Begin XCSwiftPackageProductDependency section */
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
|
EA69DC812F3C199C00592220 /* Bedrock */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
productName = Bedrock;
|
||||||
|
};
|
||||||
EA837E662F107D6800077F87 /* Bedrock */ = {
|
EA837E662F107D6800077F87 /* Bedrock */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
productName = Bedrock;
|
productName = Bedrock;
|
||||||
|
|||||||
@ -7,17 +7,17 @@
|
|||||||
<key>BusinessCard.xcscheme_^#shared#^_</key>
|
<key>BusinessCard.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>1</integer>
|
<integer>2</integer>
|
||||||
</dict>
|
</dict>
|
||||||
<key>BusinessCardClip.xcscheme_^#shared#^_</key>
|
<key>BusinessCardClip.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>2</integer>
|
<integer>0</integer>
|
||||||
</dict>
|
</dict>
|
||||||
<key>BusinessCardWatch Watch App.xcscheme_^#shared#^_</key>
|
<key>BusinessCardWatch Watch App.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>3</integer>
|
<integer>1</integer>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
|
|||||||
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.860",
|
||||||
|
"green" : "0.910",
|
||||||
|
"red" : "0.950"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.160",
|
||||||
|
"green" : "0.190",
|
||||||
|
"red" : "0.230"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.940",
|
||||||
|
"green" : "0.960",
|
||||||
|
"red" : "0.970"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.160",
|
||||||
|
"green" : "0.130",
|
||||||
|
"red" : "0.110"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "1.000",
|
||||||
|
"green" : "1.000",
|
||||||
|
"red" : "1.000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.250",
|
||||||
|
"green" : "0.200",
|
||||||
|
"red" : "0.180"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.950",
|
||||||
|
"green" : "0.950",
|
||||||
|
"red" : "0.950"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.200",
|
||||||
|
"green" : "0.160",
|
||||||
|
"red" : "0.140"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.980",
|
||||||
|
"green" : "0.980",
|
||||||
|
"red" : "0.980"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.170",
|
||||||
|
"green" : "0.140",
|
||||||
|
"red" : "0.120"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.170",
|
||||||
|
"green" : "0.140",
|
||||||
|
"red" : "0.140"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.960",
|
||||||
|
"green" : "0.940",
|
||||||
|
"red" : "0.930"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.400",
|
||||||
|
"green" : "0.340",
|
||||||
|
"red" : "0.320"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.820",
|
||||||
|
"green" : "0.770",
|
||||||
|
"red" : "0.740"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.620",
|
||||||
|
"green" : "0.580",
|
||||||
|
"red" : "0.560"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.700",
|
||||||
|
"green" : "0.640",
|
||||||
|
"red" : "0.600"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.970",
|
||||||
|
"green" : "0.950",
|
||||||
|
"red" : "0.940"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.230",
|
||||||
|
"green" : "0.200",
|
||||||
|
"red" : "0.180"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "1.000",
|
||||||
|
"green" : "0.990",
|
||||||
|
"red" : "0.980"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.300",
|
||||||
|
"green" : "0.260",
|
||||||
|
"red" : "0.240"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.920",
|
||||||
|
"green" : "0.880",
|
||||||
|
"red" : "0.860"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.360",
|
||||||
|
"green" : "0.320",
|
||||||
|
"red" : "0.300"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.470",
|
||||||
|
"green" : "0.410",
|
||||||
|
"red" : "0.380"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.750",
|
||||||
|
"green" : "0.720",
|
||||||
|
"red" : "0.700"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.170",
|
||||||
|
"green" : "0.140",
|
||||||
|
"red" : "0.120"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.970",
|
||||||
|
"green" : "0.960",
|
||||||
|
"red" : "0.960"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,6 +10,15 @@ struct BusinessCardApp: App {
|
|||||||
init() {
|
init() {
|
||||||
let schema = Schema([BusinessCard.self, Contact.self, ContactField.self])
|
let schema = Schema([BusinessCard.self, Contact.self, ContactField.self])
|
||||||
|
|
||||||
|
// Register app theme for Bedrock semantic text/surface colors.
|
||||||
|
Theme.register(
|
||||||
|
text: AppThemeText.self,
|
||||||
|
surface: AppSurface.self,
|
||||||
|
accent: AppThemeAccent.self,
|
||||||
|
status: AppStatus.self
|
||||||
|
)
|
||||||
|
Theme.register(border: AppBorder.self)
|
||||||
|
|
||||||
// Primary strategy: App Group for watch sync (without CloudKit for now)
|
// Primary strategy: App Group for watch sync (without CloudKit for now)
|
||||||
// CloudKit can be enabled once properly configured in Xcode
|
// CloudKit can be enabled once properly configured in Xcode
|
||||||
var container: ModelContainer?
|
var container: ModelContainer?
|
||||||
@ -80,7 +89,7 @@ struct BusinessCardApp: App {
|
|||||||
AppLaunchView(config: .businessCard) {
|
AppLaunchView(config: .businessCard) {
|
||||||
RootTabView()
|
RootTabView()
|
||||||
.environment(appState)
|
.environment(appState)
|
||||||
.preferredColorScheme(.light)
|
.preferredColorScheme(appState.preferredColorScheme)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,8 +2,8 @@
|
|||||||
// BusinessCardTheme.swift
|
// BusinessCardTheme.swift
|
||||||
// BusinessCard
|
// BusinessCard
|
||||||
//
|
//
|
||||||
// App-specific theme conforming to Bedrock's color protocols.
|
// App-specific adaptive theme conforming to Bedrock's color protocols.
|
||||||
// This light theme uses warm, professional tones.
|
// Uses warm light colors and deep slate dark colors.
|
||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
@ -11,39 +11,39 @@ import Bedrock
|
|||||||
|
|
||||||
// MARK: - Surface Colors
|
// MARK: - Surface Colors
|
||||||
|
|
||||||
/// Surface colors with warm off-white tones for a professional light theme.
|
/// Surface colors with warm off-white light tones and deep slate dark tones.
|
||||||
public enum BusinessCardSurfaceColors: SurfaceColorProvider {
|
public enum BusinessCardSurfaceColors: SurfaceColorProvider {
|
||||||
/// Primary background - warm off-white base
|
/// Primary background
|
||||||
public static let primary = Color(red: 0.97, green: 0.96, blue: 0.94)
|
public static let primary = Color.AppBackground.base
|
||||||
|
|
||||||
/// Secondary/elevated surface
|
/// Secondary/elevated surface
|
||||||
public static let secondary = Color(red: 0.95, green: 0.95, blue: 0.95)
|
public static let secondary = Color.AppBackground.secondary
|
||||||
|
|
||||||
/// Tertiary/card surface - most elevated
|
/// Tertiary/card surface - most elevated
|
||||||
public static let tertiary = Color(red: 1.0, green: 1.0, blue: 1.0)
|
public static let tertiary = Color.AppBackground.elevated
|
||||||
|
|
||||||
/// Overlay background (for sheets/modals)
|
/// Overlay background (for sheets/modals)
|
||||||
public static let overlay = Color(red: 0.97, green: 0.96, blue: 0.94)
|
public static let overlay = Color.AppBackground.base
|
||||||
|
|
||||||
/// Card/grouped element background
|
/// Card/grouped element background
|
||||||
public static let card = Color(red: 1.0, green: 1.0, blue: 1.0)
|
public static let card = Color.AppBackground.card
|
||||||
|
|
||||||
/// Subtle fill for grouped content sections
|
/// Subtle fill for grouped content sections
|
||||||
public static let groupedFill = Color(red: 0.95, green: 0.94, blue: 0.92)
|
public static let groupedFill = Color.AppBackground.accent
|
||||||
|
|
||||||
/// Section fill for list sections
|
/// Section fill for list sections
|
||||||
public static let sectionFill = Color(red: 0.93, green: 0.92, blue: 0.90)
|
public static let sectionFill = Color.AppBackground.secondary
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Text Colors
|
// MARK: - Text Colors
|
||||||
|
|
||||||
public enum BusinessCardTextColors: TextColorProvider {
|
public enum BusinessCardTextColors: TextColorProvider {
|
||||||
public static let primary = Color(red: 0.14, green: 0.14, blue: 0.17)
|
public static let primary = Color.AppText.primary
|
||||||
public static let secondary = Color(red: 0.32, green: 0.34, blue: 0.40)
|
public static let secondary = Color.AppText.secondary
|
||||||
public static let tertiary = Color(red: 0.56, green: 0.58, blue: 0.62)
|
public static let tertiary = Color.AppText.tertiary
|
||||||
public static let disabled = Color(red: 0.70, green: 0.72, blue: 0.75)
|
public static let disabled = Color.AppText.tertiary.opacity(Design.Opacity.strong)
|
||||||
public static let placeholder = Color(red: 0.60, green: 0.62, blue: 0.66)
|
public static let placeholder = Color.AppText.tertiary
|
||||||
public static let inverse = Color(red: 0.98, green: 0.98, blue: 0.98)
|
public static let inverse = Color.AppText.inverted
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Accent Colors
|
// MARK: - Accent Colors
|
||||||
@ -84,9 +84,9 @@ public enum BusinessCardStatusColors: StatusColorProvider {
|
|||||||
// MARK: - Border Colors
|
// MARK: - Border Colors
|
||||||
|
|
||||||
public enum BusinessCardBorderColors: BorderColorProvider {
|
public enum BusinessCardBorderColors: BorderColorProvider {
|
||||||
public static let subtle = Color(red: 0.14, green: 0.14, blue: 0.17).opacity(Design.Opacity.subtle)
|
public static let subtle = Color.AppText.tertiary.opacity(Design.Opacity.subtle)
|
||||||
public static let standard = Color(red: 0.14, green: 0.14, blue: 0.17).opacity(Design.Opacity.hint)
|
public static let standard = Color.AppText.tertiary.opacity(Design.Opacity.hint)
|
||||||
public static let emphasized = Color(red: 0.14, green: 0.14, blue: 0.17).opacity(Design.Opacity.light)
|
public static let emphasized = Color.AppText.secondary.opacity(Design.Opacity.light)
|
||||||
public static let selected = BusinessCardAccentColors.primary.opacity(Design.Opacity.medium)
|
public static let selected = BusinessCardAccentColors.primary.opacity(Design.Opacity.medium)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -105,15 +105,14 @@ extension Design.Shadow {
|
|||||||
/// BusinessCard's light theme color palette.
|
/// BusinessCard's light theme color palette.
|
||||||
/// Uses warm, professional tones suitable for a business card app.
|
/// Uses warm, professional tones suitable for a business card app.
|
||||||
extension Color {
|
extension Color {
|
||||||
|
// MARK: - App Backgrounds
|
||||||
// MARK: - App Backgrounds (Light Theme)
|
|
||||||
|
|
||||||
enum AppBackground {
|
enum AppBackground {
|
||||||
static let base = Color(red: 0.97, green: 0.96, blue: 0.94)
|
static let base = Color("AppBackgroundBase")
|
||||||
static let secondary = Color(red: 0.95, green: 0.95, blue: 0.95)
|
static let secondary = Color("AppBackgroundSecondary")
|
||||||
static let elevated = Color(red: 1.0, green: 1.0, blue: 1.0)
|
static let elevated = Color("AppBackgroundElevated")
|
||||||
static let card = Color(red: 1.0, green: 1.0, blue: 1.0)
|
static let card = elevated
|
||||||
static let accent = Color(red: 0.95, green: 0.91, blue: 0.86)
|
static let accent = Color("AppBackgroundAccent")
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Card Theme Palette
|
// MARK: - Card Theme Palette
|
||||||
@ -142,13 +141,13 @@ extension Color {
|
|||||||
static let slate = Color(red: 0.29, green: 0.33, blue: 0.4)
|
static let slate = Color(red: 0.29, green: 0.33, blue: 0.4)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - App Text Colors (Light Theme)
|
// MARK: - App Text Colors
|
||||||
|
|
||||||
enum AppText {
|
enum AppText {
|
||||||
static let primary = Color(red: 0.14, green: 0.14, blue: 0.17)
|
static let primary = Color("AppTextPrimary")
|
||||||
static let secondary = Color(red: 0.32, green: 0.34, blue: 0.4)
|
static let secondary = Color("AppTextSecondary")
|
||||||
static let tertiary = Color(red: 0.56, green: 0.58, blue: 0.62)
|
static let tertiary = Color("AppTextTertiary")
|
||||||
static let inverted = Color(red: 0.98, green: 0.98, blue: 0.98)
|
static let inverted = Color("AppTextInverted")
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Badge Colors
|
// MARK: - Badge Colors
|
||||||
@ -158,14 +157,14 @@ extension Color {
|
|||||||
static let neutral = Color(red: 0.89, green: 0.89, blue: 0.9)
|
static let neutral = Color(red: 0.89, green: 0.89, blue: 0.9)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Share Sheet Dark Theme
|
// MARK: - Share Sheet Theme
|
||||||
|
|
||||||
enum ShareSheet {
|
enum ShareSheet {
|
||||||
static let background = Color(red: 0.18, green: 0.20, blue: 0.23)
|
static let background = Color("ShareSheetBackground")
|
||||||
static let cardBackground = Color(red: 0.24, green: 0.26, blue: 0.30)
|
static let cardBackground = Color("ShareSheetCardBackground")
|
||||||
static let rowBackground = Color(red: 0.30, green: 0.32, blue: 0.36)
|
static let rowBackground = Color("ShareSheetRowBackground")
|
||||||
static let text = Color(red: 0.96, green: 0.96, blue: 0.97)
|
static let text = Color("ShareSheetText")
|
||||||
static let secondaryText = Color(red: 0.70, green: 0.72, blue: 0.75)
|
static let secondaryText = Color("ShareSheetSecondaryText")
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Social Media Brand Colors
|
// MARK: - Social Media Brand Colors
|
||||||
|
|||||||
@ -1,19 +1,53 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import Observation
|
import Observation
|
||||||
import SwiftData
|
import SwiftData
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
enum AppAppearance: String, CaseIterable, Sendable {
|
||||||
|
case system
|
||||||
|
case light
|
||||||
|
case dark
|
||||||
|
|
||||||
|
var preferredColorScheme: ColorScheme? {
|
||||||
|
switch self {
|
||||||
|
case .system: nil
|
||||||
|
case .light: .light
|
||||||
|
case .dark: .dark
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Observable
|
@Observable
|
||||||
@MainActor
|
@MainActor
|
||||||
final class AppState {
|
final class AppState {
|
||||||
|
private enum DefaultsKey {
|
||||||
|
static let appearance = "appAppearance"
|
||||||
|
}
|
||||||
|
|
||||||
var selectedTab: AppTab = .cards
|
var selectedTab: AppTab = .cards
|
||||||
var cardStore: CardStore
|
var cardStore: CardStore
|
||||||
var contactsStore: ContactsStore
|
var contactsStore: ContactsStore
|
||||||
let shareLinkService: ShareLinkProviding
|
let shareLinkService: ShareLinkProviding
|
||||||
|
var appearance: AppAppearance {
|
||||||
|
didSet {
|
||||||
|
UserDefaults.standard.set(appearance.rawValue, forKey: DefaultsKey.appearance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var preferredColorScheme: ColorScheme? {
|
||||||
|
appearance.preferredColorScheme
|
||||||
|
}
|
||||||
|
|
||||||
init(modelContext: ModelContext) {
|
init(modelContext: ModelContext) {
|
||||||
self.cardStore = CardStore(modelContext: modelContext)
|
self.cardStore = CardStore(modelContext: modelContext)
|
||||||
self.contactsStore = ContactsStore(modelContext: modelContext)
|
self.contactsStore = ContactsStore(modelContext: modelContext)
|
||||||
self.shareLinkService = ShareLinkService()
|
self.shareLinkService = ShareLinkService()
|
||||||
|
if let rawValue = UserDefaults.standard.string(forKey: DefaultsKey.appearance),
|
||||||
|
let savedAppearance = AppAppearance(rawValue: rawValue) {
|
||||||
|
self.appearance = savedAppearance
|
||||||
|
} else {
|
||||||
|
self.appearance = .system
|
||||||
|
}
|
||||||
|
|
||||||
// Clean up expired shared cards on launch (best-effort, non-blocking)
|
// Clean up expired shared cards on launch (best-effort, non-blocking)
|
||||||
Task {
|
Task {
|
||||||
|
|||||||
@ -76,9 +76,9 @@ private struct ProfileBannerContent: View {
|
|||||||
|
|
||||||
VStack(spacing: Design.Spacing.xSmall) {
|
VStack(spacing: Design.Spacing.xSmall) {
|
||||||
Image(systemName: "person.fill")
|
Image(systemName: "person.fill")
|
||||||
.font(.system(size: Design.BaseFontSize.display, weight: .bold))
|
.typography(.title2Bold)
|
||||||
Text("Profile")
|
Text("Profile")
|
||||||
.font(.title3)
|
.typography(.title3)
|
||||||
.bold()
|
.bold()
|
||||||
}
|
}
|
||||||
.foregroundStyle(card.theme.textColor.opacity(Design.Opacity.medium))
|
.foregroundStyle(card.theme.textColor.opacity(Design.Opacity.medium))
|
||||||
@ -111,9 +111,9 @@ private struct LogoBannerContent: View {
|
|||||||
} else {
|
} else {
|
||||||
VStack(spacing: Design.Spacing.xSmall) {
|
VStack(spacing: Design.Spacing.xSmall) {
|
||||||
Image(systemName: "building.2.fill")
|
Image(systemName: "building.2.fill")
|
||||||
.font(.system(size: Design.BaseFontSize.display, weight: .bold))
|
.typography(.title2Bold)
|
||||||
Text("Logo")
|
Text("Logo")
|
||||||
.font(.title3)
|
.typography(.title3)
|
||||||
.bold()
|
.bold()
|
||||||
}
|
}
|
||||||
.foregroundStyle(card.theme.textColor.opacity(Design.Opacity.medium))
|
.foregroundStyle(card.theme.textColor.opacity(Design.Opacity.medium))
|
||||||
@ -146,9 +146,9 @@ private struct CoverBannerContent: View {
|
|||||||
|
|
||||||
VStack(spacing: Design.Spacing.xSmall) {
|
VStack(spacing: Design.Spacing.xSmall) {
|
||||||
Image(systemName: "photo.fill")
|
Image(systemName: "photo.fill")
|
||||||
.font(.system(size: Design.BaseFontSize.display, weight: .bold))
|
.typography(.title2Bold)
|
||||||
Text("Cover")
|
Text("Cover")
|
||||||
.font(.title3)
|
.typography(.title3)
|
||||||
.bold()
|
.bold()
|
||||||
}
|
}
|
||||||
.foregroundStyle(card.theme.textColor.opacity(Design.Opacity.medium))
|
.foregroundStyle(card.theme.textColor.opacity(Design.Opacity.medium))
|
||||||
@ -173,28 +173,28 @@ private struct CardContentView: View {
|
|||||||
VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) {
|
VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) {
|
||||||
HStack(spacing: Design.Spacing.xSmall) {
|
HStack(spacing: Design.Spacing.xSmall) {
|
||||||
Text(card.fullName)
|
Text(card.fullName)
|
||||||
.font(.title2)
|
.typography(.title2)
|
||||||
.bold()
|
.bold()
|
||||||
.foregroundStyle(textColor)
|
.foregroundStyle(textColor)
|
||||||
|
|
||||||
if !card.pronouns.isEmpty {
|
if !card.pronouns.isEmpty {
|
||||||
Text("(\(card.pronouns))")
|
Text("(\(card.pronouns))")
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Text(card.role)
|
Text(card.role)
|
||||||
.font(.headline)
|
.typography(.heading)
|
||||||
.foregroundStyle(textColor)
|
.foregroundStyle(textColor)
|
||||||
|
|
||||||
Text(card.company)
|
Text(card.company)
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
|
|
||||||
if !card.headline.isEmpty {
|
if !card.headline.isEmpty {
|
||||||
Text(card.headline)
|
Text(card.headline)
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
.padding(.top, Design.Spacing.xxSmall)
|
.padding(.top, Design.Spacing.xxSmall)
|
||||||
}
|
}
|
||||||
@ -249,7 +249,7 @@ private struct ProfileAvatarView: View {
|
|||||||
.scaledToFill()
|
.scaledToFill()
|
||||||
} else {
|
} else {
|
||||||
Image(systemName: card.avatarSystemName)
|
Image(systemName: card.avatarSystemName)
|
||||||
.font(.system(size: Design.BaseFontSize.title))
|
.typography(.title3)
|
||||||
.foregroundStyle(card.theme.textColor)
|
.foregroundStyle(card.theme.textColor)
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
.background(card.theme.accentColor)
|
.background(card.theme.accentColor)
|
||||||
@ -277,9 +277,9 @@ private struct LogoBadgeView: View {
|
|||||||
} else {
|
} else {
|
||||||
VStack(spacing: Design.Spacing.xxSmall) {
|
VStack(spacing: Design.Spacing.xxSmall) {
|
||||||
Image(systemName: "building.2")
|
Image(systemName: "building.2")
|
||||||
.font(.system(size: Design.BaseFontSize.body))
|
.typography(.body)
|
||||||
Text("Logo")
|
Text("Logo")
|
||||||
.font(.caption2)
|
.typography(.caption2)
|
||||||
}
|
}
|
||||||
.foregroundStyle(card.theme.textColor)
|
.foregroundStyle(card.theme.textColor)
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
@ -310,9 +310,9 @@ private struct LogoRectangleView: View {
|
|||||||
} else {
|
} else {
|
||||||
VStack(spacing: Design.Spacing.xxSmall) {
|
VStack(spacing: Design.Spacing.xxSmall) {
|
||||||
Image(systemName: "building.2")
|
Image(systemName: "building.2")
|
||||||
.font(.system(size: Design.BaseFontSize.body))
|
.typography(.body)
|
||||||
Text("Logo")
|
Text("Logo")
|
||||||
.font(.caption2)
|
.typography(.caption2)
|
||||||
}
|
}
|
||||||
.foregroundStyle(card.theme.textColor)
|
.foregroundStyle(card.theme.textColor)
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
@ -354,7 +354,7 @@ private struct ContactFieldRowView: View {
|
|||||||
Button(action: action) {
|
Button(action: action) {
|
||||||
HStack(alignment: .top, spacing: Design.Spacing.medium) {
|
HStack(alignment: .top, spacing: Design.Spacing.medium) {
|
||||||
field.iconImage()
|
field.iconImage()
|
||||||
.font(.body)
|
.typography(.body)
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.frame(width: Design.CardSize.socialIconSize, height: Design.CardSize.socialIconSize)
|
.frame(width: Design.CardSize.socialIconSize, height: Design.CardSize.socialIconSize)
|
||||||
.background(themeColor)
|
.background(themeColor)
|
||||||
@ -362,12 +362,12 @@ private struct ContactFieldRowView: View {
|
|||||||
|
|
||||||
VStack(alignment: .leading, spacing: 0) {
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
Text(field.displayValue)
|
Text(field.displayValue)
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
.multilineTextAlignment(.leading)
|
.multilineTextAlignment(.leading)
|
||||||
|
|
||||||
Text(field.title.isEmpty ? field.displayName : field.title)
|
Text(field.title.isEmpty ? field.displayName : field.title)
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
}
|
}
|
||||||
@ -375,7 +375,7 @@ private struct ContactFieldRowView: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Image(systemName: "chevron.right")
|
Image(systemName: "chevron.right")
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(Color.Text.tertiary)
|
.foregroundStyle(Color.Text.tertiary)
|
||||||
}
|
}
|
||||||
.contentShape(.rect)
|
.contentShape(.rect)
|
||||||
|
|||||||
@ -408,7 +408,7 @@ private struct CustomColorSwatch: View {
|
|||||||
// Center icon to indicate it's a picker
|
// Center icon to indicate it's a picker
|
||||||
if customColor == nil {
|
if customColor == nil {
|
||||||
Image(systemName: "eyedropper")
|
Image(systemName: "eyedropper")
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.shadow(color: .black.opacity(Design.Opacity.medium), radius: Design.Shadow.radiusSmall)
|
.shadow(color: .black.opacity(Design.Opacity.medium), radius: Design.Shadow.radiusSmall)
|
||||||
}
|
}
|
||||||
@ -443,7 +443,7 @@ private struct CustomColorPickerSheet: View {
|
|||||||
.frame(height: Design.CardSize.bannerHeight)
|
.frame(height: Design.CardSize.bannerHeight)
|
||||||
.overlay(
|
.overlay(
|
||||||
Text("Preview")
|
Text("Preview")
|
||||||
.font(.headline)
|
.typography(.heading)
|
||||||
.foregroundStyle(selectedColor.contrastingTextColor)
|
.foregroundStyle(selectedColor.contrastingTextColor)
|
||||||
)
|
)
|
||||||
.padding(.horizontal, Design.Spacing.large)
|
.padding(.horizontal, Design.Spacing.large)
|
||||||
@ -572,24 +572,24 @@ private struct ImageLayoutRow: View {
|
|||||||
Button(action: onSelectLayout) {
|
Button(action: onSelectLayout) {
|
||||||
HStack(spacing: Design.Spacing.medium) {
|
HStack(spacing: Design.Spacing.medium) {
|
||||||
Image(systemName: selectedHeaderLayout.iconName)
|
Image(systemName: selectedHeaderLayout.iconName)
|
||||||
.font(.title3)
|
.typography(.title3)
|
||||||
.foregroundStyle(Color.accentColor)
|
.foregroundStyle(Color.accentColor)
|
||||||
.frame(width: Design.CardSize.socialIconSize)
|
.frame(width: Design.CardSize.socialIconSize)
|
||||||
|
|
||||||
VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) {
|
VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) {
|
||||||
Text("Header Layout")
|
Text("Header Layout")
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
|
|
||||||
Text(selectedHeaderLayout.displayName)
|
Text(selectedHeaderLayout.displayName)
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Image(systemName: "chevron.right")
|
Image(systemName: "chevron.right")
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(Color.Text.tertiary)
|
.foregroundStyle(Color.Text.tertiary)
|
||||||
}
|
}
|
||||||
.padding(.vertical, Design.Spacing.xSmall)
|
.padding(.vertical, Design.Spacing.xSmall)
|
||||||
@ -654,9 +654,9 @@ private struct EditorBannerPreviewView: View {
|
|||||||
|
|
||||||
VStack(spacing: Design.Spacing.xSmall) {
|
VStack(spacing: Design.Spacing.xSmall) {
|
||||||
Image(systemName: "person.fill")
|
Image(systemName: "person.fill")
|
||||||
.font(.system(size: Design.BaseFontSize.display, weight: .bold))
|
.typography(.title2Bold)
|
||||||
Text("Profile")
|
Text("Profile")
|
||||||
.font(.title3)
|
.typography(.title3)
|
||||||
.bold()
|
.bold()
|
||||||
}
|
}
|
||||||
.foregroundStyle(selectedTheme.textColor.opacity(Design.Opacity.medium))
|
.foregroundStyle(selectedTheme.textColor.opacity(Design.Opacity.medium))
|
||||||
@ -678,9 +678,9 @@ private struct EditorBannerPreviewView: View {
|
|||||||
} else {
|
} else {
|
||||||
VStack(spacing: Design.Spacing.xSmall) {
|
VStack(spacing: Design.Spacing.xSmall) {
|
||||||
Image(systemName: "building.2.fill")
|
Image(systemName: "building.2.fill")
|
||||||
.font(.system(size: Design.BaseFontSize.display, weight: .bold))
|
.typography(.title2Bold)
|
||||||
Text("Logo")
|
Text("Logo")
|
||||||
.font(.title3)
|
.typography(.title3)
|
||||||
.bold()
|
.bold()
|
||||||
}
|
}
|
||||||
.foregroundStyle(selectedTheme.textColor.opacity(Design.Opacity.medium))
|
.foregroundStyle(selectedTheme.textColor.opacity(Design.Opacity.medium))
|
||||||
@ -703,9 +703,9 @@ private struct EditorBannerPreviewView: View {
|
|||||||
|
|
||||||
VStack(spacing: Design.Spacing.xSmall) {
|
VStack(spacing: Design.Spacing.xSmall) {
|
||||||
Image(systemName: "photo.fill")
|
Image(systemName: "photo.fill")
|
||||||
.font(.system(size: Design.BaseFontSize.display, weight: .bold))
|
.typography(.title2Bold)
|
||||||
Text("Cover")
|
Text("Cover")
|
||||||
.font(.title3)
|
.typography(.title3)
|
||||||
.bold()
|
.bold()
|
||||||
}
|
}
|
||||||
.foregroundStyle(selectedTheme.textColor.opacity(Design.Opacity.medium))
|
.foregroundStyle(selectedTheme.textColor.opacity(Design.Opacity.medium))
|
||||||
@ -742,7 +742,7 @@ private struct EditorLogoBadgeView: View {
|
|||||||
.padding(Design.Spacing.small)
|
.padding(Design.Spacing.small)
|
||||||
} else {
|
} else {
|
||||||
Image(systemName: "building.2")
|
Image(systemName: "building.2")
|
||||||
.font(.system(size: Design.BaseFontSize.title))
|
.typography(.title3)
|
||||||
.foregroundStyle(theme.textColor)
|
.foregroundStyle(theme.textColor)
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
}
|
}
|
||||||
@ -781,7 +781,7 @@ private struct EditorLogoRectangleView: View {
|
|||||||
.padding(Design.Spacing.small)
|
.padding(Design.Spacing.small)
|
||||||
} else {
|
} else {
|
||||||
Image(systemName: "building.2")
|
Image(systemName: "building.2")
|
||||||
.font(.system(size: Design.BaseFontSize.title))
|
.typography(.title3)
|
||||||
.foregroundStyle(theme.textColor)
|
.foregroundStyle(theme.textColor)
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
}
|
}
|
||||||
@ -859,24 +859,24 @@ private struct ImageActionRow: View {
|
|||||||
Button(action: onTap) {
|
Button(action: onTap) {
|
||||||
HStack(spacing: Design.Spacing.medium) {
|
HStack(spacing: Design.Spacing.medium) {
|
||||||
Image(systemName: systemImage)
|
Image(systemName: systemImage)
|
||||||
.font(.title3)
|
.typography(.title3)
|
||||||
.foregroundStyle(hasImage ? Color.accentColor : Color.Text.secondary)
|
.foregroundStyle(hasImage ? Color.accentColor : Color.Text.secondary)
|
||||||
.frame(width: Design.CardSize.socialIconSize)
|
.frame(width: Design.CardSize.socialIconSize)
|
||||||
|
|
||||||
VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) {
|
VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) {
|
||||||
Text(title)
|
Text(title)
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
|
|
||||||
Text(subtitle)
|
Text(subtitle)
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Image(systemName: "chevron.right")
|
Image(systemName: "chevron.right")
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(Color.Text.tertiary)
|
.foregroundStyle(Color.Text.tertiary)
|
||||||
}
|
}
|
||||||
.padding(.vertical, Design.Spacing.xSmall)
|
.padding(.vertical, Design.Spacing.xSmall)
|
||||||
@ -909,7 +909,7 @@ private struct ProfilePhotoView: View {
|
|||||||
.scaledToFill()
|
.scaledToFill()
|
||||||
} else {
|
} else {
|
||||||
Image(systemName: avatarSystemName)
|
Image(systemName: avatarSystemName)
|
||||||
.font(.title)
|
.typography(.title)
|
||||||
.foregroundStyle(theme.textColor)
|
.foregroundStyle(theme.textColor)
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
.background(theme.accentColor)
|
.background(theme.accentColor)
|
||||||
@ -933,7 +933,7 @@ private struct ContactFieldRowView: View {
|
|||||||
HStack(spacing: Design.Spacing.medium) {
|
HStack(spacing: Design.Spacing.medium) {
|
||||||
// Drag handle
|
// Drag handle
|
||||||
Image(systemName: "line.3.horizontal")
|
Image(systemName: "line.3.horizontal")
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.Text.tertiary)
|
.foregroundStyle(Color.Text.tertiary)
|
||||||
.accessibilityHidden(true)
|
.accessibilityHidden(true)
|
||||||
|
|
||||||
@ -943,19 +943,19 @@ private struct ContactFieldRowView: View {
|
|||||||
.frame(width: Design.CardSize.avatarSize, height: Design.CardSize.avatarSize)
|
.frame(width: Design.CardSize.avatarSize, height: Design.CardSize.avatarSize)
|
||||||
.overlay(
|
.overlay(
|
||||||
field.fieldType.iconImage()
|
field.fieldType.iconImage()
|
||||||
.font(.title3)
|
.typography(.title3)
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Content
|
// Content
|
||||||
VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) {
|
VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) {
|
||||||
Text(field.value.isEmpty ? field.fieldType.valuePlaceholder : field.shortDisplayValue)
|
Text(field.value.isEmpty ? field.fieldType.valuePlaceholder : field.shortDisplayValue)
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(field.value.isEmpty ? Color.Text.secondary : Color.Text.primary)
|
.foregroundStyle(field.value.isEmpty ? Color.Text.secondary : Color.Text.primary)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
|
|
||||||
Text(field.title.isEmpty ? field.fieldType.displayName : field.title)
|
Text(field.title.isEmpty ? field.fieldType.displayName : field.title)
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
}
|
}
|
||||||
@ -992,7 +992,7 @@ private struct AccreditationsRow: View {
|
|||||||
addAccreditation()
|
addAccreditation()
|
||||||
} label: {
|
} label: {
|
||||||
Image(systemName: "plus.circle.fill")
|
Image(systemName: "plus.circle.fill")
|
||||||
.font(.title2)
|
.typography(.title2)
|
||||||
.foregroundStyle(Color.accentColor)
|
.foregroundStyle(Color.accentColor)
|
||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
@ -1006,12 +1006,12 @@ private struct AccreditationsRow: View {
|
|||||||
ForEach(accreditationsList, id: \.self) { tag in
|
ForEach(accreditationsList, id: \.self) { tag in
|
||||||
HStack(spacing: Design.Spacing.xSmall) {
|
HStack(spacing: Design.Spacing.xSmall) {
|
||||||
Text(tag)
|
Text(tag)
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
Button {
|
Button {
|
||||||
removeAccreditation(tag)
|
removeAccreditation(tag)
|
||||||
} label: {
|
} label: {
|
||||||
Image(systemName: "xmark.circle.fill")
|
Image(systemName: "xmark.circle.fill")
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(Color.secondary)
|
.foregroundStyle(Color.secondary)
|
||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
@ -1052,7 +1052,7 @@ private struct PreviewCardButton: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
Button(action: action) {
|
Button(action: action) {
|
||||||
Text("Preview card")
|
Text("Preview card")
|
||||||
.font(.headline)
|
.typography(.heading)
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
.padding(Design.Spacing.medium)
|
.padding(Design.Spacing.medium)
|
||||||
|
|||||||
@ -118,17 +118,17 @@ private struct EmptyCardsView: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Image(systemName: "rectangle.stack.badge.plus")
|
Image(systemName: "rectangle.stack.badge.plus")
|
||||||
.font(.system(size: Design.BaseFontSize.display))
|
.typography(.title2)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
|
|
||||||
VStack(spacing: Design.Spacing.small) {
|
VStack(spacing: Design.Spacing.small) {
|
||||||
Text("Create your first card")
|
Text("Create your first card")
|
||||||
.font(.title2)
|
.typography(.title2)
|
||||||
.bold()
|
.bold()
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
|
|
||||||
Text("Design and share polished digital business cards for every context.")
|
Text("Design and share polished digital business cards for every context.")
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,12 +49,12 @@ struct ActionRowContent: View {
|
|||||||
|
|
||||||
VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) {
|
VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) {
|
||||||
Text(title)
|
Text(title)
|
||||||
.font(.headline)
|
.typography(.heading)
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
|
|
||||||
if let subtitle {
|
if let subtitle {
|
||||||
Text(subtitle)
|
Text(subtitle)
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -105,19 +105,19 @@ private struct FieldRowPreview: View {
|
|||||||
.frame(width: Design.CardSize.avatarSize, height: Design.CardSize.avatarSize)
|
.frame(width: Design.CardSize.avatarSize, height: Design.CardSize.avatarSize)
|
||||||
.overlay(
|
.overlay(
|
||||||
field.fieldType.iconImage()
|
field.fieldType.iconImage()
|
||||||
.font(.title3)
|
.typography(.title3)
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
)
|
)
|
||||||
|
|
||||||
VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) {
|
VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) {
|
||||||
Text(field.value.isEmpty ? field.fieldType.displayName : field.shortDisplayValue)
|
Text(field.value.isEmpty ? field.fieldType.displayName : field.shortDisplayValue)
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
|
|
||||||
if !field.title.isEmpty {
|
if !field.title.isEmpty {
|
||||||
Text(field.title)
|
Text(field.title)
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
}
|
}
|
||||||
@ -141,7 +141,7 @@ private struct FieldRow: View {
|
|||||||
HStack(spacing: Design.Spacing.medium) {
|
HStack(spacing: Design.Spacing.medium) {
|
||||||
// Drag handle
|
// Drag handle
|
||||||
Image(systemName: "line.3.horizontal")
|
Image(systemName: "line.3.horizontal")
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(Color.Text.tertiary)
|
.foregroundStyle(Color.Text.tertiary)
|
||||||
.frame(width: Design.Spacing.large)
|
.frame(width: Design.Spacing.large)
|
||||||
|
|
||||||
@ -151,7 +151,7 @@ private struct FieldRow: View {
|
|||||||
.frame(width: Design.CardSize.avatarSize, height: Design.CardSize.avatarSize)
|
.frame(width: Design.CardSize.avatarSize, height: Design.CardSize.avatarSize)
|
||||||
.overlay(
|
.overlay(
|
||||||
field.fieldType.iconImage()
|
field.fieldType.iconImage()
|
||||||
.font(.title3)
|
.typography(.title3)
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -159,12 +159,12 @@ private struct FieldRow: View {
|
|||||||
Button(action: onTap) {
|
Button(action: onTap) {
|
||||||
VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) {
|
VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) {
|
||||||
Text(field.value.isEmpty ? field.fieldType.valuePlaceholder : field.shortDisplayValue)
|
Text(field.value.isEmpty ? field.fieldType.valuePlaceholder : field.shortDisplayValue)
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(field.value.isEmpty ? Color.Text.secondary : Color.Text.primary)
|
.foregroundStyle(field.value.isEmpty ? Color.Text.secondary : Color.Text.primary)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
|
|
||||||
Text(field.title.isEmpty ? field.fieldType.displayName : field.title)
|
Text(field.title.isEmpty ? field.fieldType.displayName : field.title)
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
}
|
}
|
||||||
@ -175,7 +175,7 @@ private struct FieldRow: View {
|
|||||||
// Delete button
|
// Delete button
|
||||||
Button(action: onDelete) {
|
Button(action: onDelete) {
|
||||||
Image(systemName: "xmark.circle.fill")
|
Image(systemName: "xmark.circle.fill")
|
||||||
.font(.title3)
|
.typography(.title3)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
|
|||||||
@ -68,7 +68,7 @@ private struct AddressTextField: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
|
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
|
||||||
Text(label)
|
Text(label)
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
|
|
||||||
TextField(placeholder, text: $text)
|
TextField(placeholder, text: $text)
|
||||||
@ -98,7 +98,7 @@ private struct AddressTextField: View {
|
|||||||
|
|
||||||
Section("Preview") {
|
Section("Preview") {
|
||||||
Text(address.formattedString)
|
Text(address.formattedString)
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,13 +12,13 @@ struct ContactFieldPickerView: View {
|
|||||||
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
|
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
|
||||||
HStack {
|
HStack {
|
||||||
Text("Tap a field below to add it")
|
Text("Tap a field below to add it")
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Image(systemName: "plus")
|
Image(systemName: "plus")
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
}
|
}
|
||||||
.padding(.horizontal, Design.Spacing.medium)
|
.padding(.horizontal, Design.Spacing.medium)
|
||||||
@ -52,12 +52,12 @@ private struct FieldTypeButton: View {
|
|||||||
.frame(width: Design.CardSize.avatarSize, height: Design.CardSize.avatarSize)
|
.frame(width: Design.CardSize.avatarSize, height: Design.CardSize.avatarSize)
|
||||||
.overlay(
|
.overlay(
|
||||||
fieldType.iconImage()
|
fieldType.iconImage()
|
||||||
.font(.title3)
|
.typography(.title3)
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
)
|
)
|
||||||
|
|
||||||
Text(fieldType.displayName)
|
Text(fieldType.displayName)
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.lineLimit(2)
|
.lineLimit(2)
|
||||||
|
|||||||
@ -88,7 +88,7 @@ struct HeaderLayoutPickerView: View {
|
|||||||
dismiss()
|
dismiss()
|
||||||
} label: {
|
} label: {
|
||||||
Text("Confirm layout")
|
Text("Confirm layout")
|
||||||
.font(.headline)
|
.typography(.heading)
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
.padding(.vertical, Design.Spacing.large)
|
.padding(.vertical, Design.Spacing.large)
|
||||||
@ -107,7 +107,7 @@ struct HeaderLayoutPickerView: View {
|
|||||||
dismiss()
|
dismiss()
|
||||||
} label: {
|
} label: {
|
||||||
Image(systemName: "xmark")
|
Image(systemName: "xmark")
|
||||||
.font(.body)
|
.typography(.body)
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -206,9 +206,9 @@ private struct LayoutPreviewCard: View {
|
|||||||
|
|
||||||
VStack(spacing: Design.Spacing.xxSmall) {
|
VStack(spacing: Design.Spacing.xxSmall) {
|
||||||
Image(systemName: "person.fill")
|
Image(systemName: "person.fill")
|
||||||
.font(.system(size: Design.BaseFontSize.title))
|
.typography(.title3)
|
||||||
Text("Profile")
|
Text("Profile")
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.bold()
|
.bold()
|
||||||
}
|
}
|
||||||
.foregroundStyle(theme.textColor.opacity(Design.Opacity.medium))
|
.foregroundStyle(theme.textColor.opacity(Design.Opacity.medium))
|
||||||
@ -227,9 +227,9 @@ private struct LayoutPreviewCard: View {
|
|||||||
} else {
|
} else {
|
||||||
VStack(spacing: Design.Spacing.xxSmall) {
|
VStack(spacing: Design.Spacing.xxSmall) {
|
||||||
Image(systemName: "building.2.fill")
|
Image(systemName: "building.2.fill")
|
||||||
.font(.system(size: Design.BaseFontSize.title))
|
.typography(.title3)
|
||||||
Text("Logo")
|
Text("Logo")
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.bold()
|
.bold()
|
||||||
}
|
}
|
||||||
.foregroundStyle(theme.textColor.opacity(Design.Opacity.medium))
|
.foregroundStyle(theme.textColor.opacity(Design.Opacity.medium))
|
||||||
@ -249,9 +249,9 @@ private struct LayoutPreviewCard: View {
|
|||||||
|
|
||||||
VStack(spacing: Design.Spacing.xxSmall) {
|
VStack(spacing: Design.Spacing.xxSmall) {
|
||||||
Image(systemName: "photo.fill")
|
Image(systemName: "photo.fill")
|
||||||
.font(.system(size: Design.BaseFontSize.title))
|
.typography(.title3)
|
||||||
Text("Cover")
|
Text("Cover")
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.bold()
|
.bold()
|
||||||
}
|
}
|
||||||
.foregroundStyle(Color.Text.tertiary)
|
.foregroundStyle(Color.Text.tertiary)
|
||||||
@ -351,9 +351,9 @@ private struct LayoutPreviewCard: View {
|
|||||||
} else {
|
} else {
|
||||||
VStack(spacing: Design.Spacing.xxSmall) {
|
VStack(spacing: Design.Spacing.xxSmall) {
|
||||||
Image(systemName: "building.2")
|
Image(systemName: "building.2")
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
Text("Logo")
|
Text("Logo")
|
||||||
.font(.system(size: 8))
|
.typography(.caption2)
|
||||||
}
|
}
|
||||||
.foregroundStyle(theme.textColor)
|
.foregroundStyle(theme.textColor)
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
@ -375,9 +375,9 @@ private struct LayoutPreviewCard: View {
|
|||||||
} else {
|
} else {
|
||||||
VStack(spacing: Design.Spacing.xxSmall) {
|
VStack(spacing: Design.Spacing.xxSmall) {
|
||||||
Image(systemName: "building.2")
|
Image(systemName: "building.2")
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
Text("Logo")
|
Text("Logo")
|
||||||
.font(.system(size: 8))
|
.typography(.caption2)
|
||||||
}
|
}
|
||||||
.foregroundStyle(theme.textColor)
|
.foregroundStyle(theme.textColor)
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
@ -411,9 +411,9 @@ private struct LayoutBadge: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(spacing: Design.Spacing.xSmall) {
|
HStack(spacing: Design.Spacing.xSmall) {
|
||||||
Image(systemName: iconName)
|
Image(systemName: iconName)
|
||||||
.font(.caption2)
|
.typography(.caption2)
|
||||||
Text(text)
|
Text(text)
|
||||||
.font(.caption2)
|
.typography(.caption2)
|
||||||
.bold()
|
.bold()
|
||||||
}
|
}
|
||||||
.padding(.horizontal, Design.Spacing.small)
|
.padding(.horizontal, Design.Spacing.small)
|
||||||
|
|||||||
@ -11,10 +11,10 @@ struct IconRowView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(spacing: Design.Spacing.xSmall) {
|
HStack(spacing: Design.Spacing.xSmall) {
|
||||||
Image(systemName: systemImage)
|
Image(systemName: systemImage)
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(textColor.opacity(Design.Opacity.heavy))
|
.foregroundStyle(textColor.opacity(Design.Opacity.heavy))
|
||||||
Text(text)
|
Text(text)
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(textColor)
|
.foregroundStyle(textColor)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -121,7 +121,7 @@ struct ImageEditorFlow: View {
|
|||||||
onComplete(nil)
|
onComplete(nil)
|
||||||
} label: {
|
} label: {
|
||||||
Image(systemName: "xmark")
|
Image(systemName: "xmark")
|
||||||
.font(.body.bold())
|
.typography(.bodyEmphasis)
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -319,12 +319,12 @@ private struct OptionRow: View {
|
|||||||
Button(action: action) {
|
Button(action: action) {
|
||||||
HStack(spacing: Design.Spacing.medium) {
|
HStack(spacing: Design.Spacing.medium) {
|
||||||
Image(systemName: icon)
|
Image(systemName: icon)
|
||||||
.font(.body)
|
.typography(.body)
|
||||||
.foregroundStyle(isDestructive ? Color.red : Color.Text.secondary)
|
.foregroundStyle(isDestructive ? Color.red : Color.Text.secondary)
|
||||||
.frame(width: Design.CardSize.socialIconSize)
|
.frame(width: Design.CardSize.socialIconSize)
|
||||||
|
|
||||||
Text(title)
|
Text(title)
|
||||||
.font(.body)
|
.typography(.body)
|
||||||
.foregroundStyle(isDestructive ? Color.red : Color.Text.primary)
|
.foregroundStyle(isDestructive ? Color.red : Color.Text.primary)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|||||||
@ -9,7 +9,7 @@ struct LabelBadgeView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Text(String.localized(label))
|
Text(String.localized(label))
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.bold()
|
.bold()
|
||||||
.foregroundStyle(textColor)
|
.foregroundStyle(textColor)
|
||||||
.padding(.horizontal, Design.Spacing.small)
|
.padding(.horizontal, Design.Spacing.small)
|
||||||
|
|||||||
@ -116,7 +116,7 @@ struct PhotoSourcePicker: View {
|
|||||||
dismiss()
|
dismiss()
|
||||||
} label: {
|
} label: {
|
||||||
Image(systemName: "xmark")
|
Image(systemName: "xmark")
|
||||||
.font(.body.bold())
|
.typography(.bodyEmphasis)
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -139,12 +139,12 @@ private struct OptionRow: View {
|
|||||||
Button(action: action) {
|
Button(action: action) {
|
||||||
HStack(spacing: Design.Spacing.medium) {
|
HStack(spacing: Design.Spacing.medium) {
|
||||||
Image(systemName: icon)
|
Image(systemName: icon)
|
||||||
.font(.body)
|
.typography(.body)
|
||||||
.foregroundStyle(isDestructive ? Color.red : Color.Text.secondary)
|
.foregroundStyle(isDestructive ? Color.red : Color.Text.secondary)
|
||||||
.frame(width: Design.CardSize.socialIconSize)
|
.frame(width: Design.CardSize.socialIconSize)
|
||||||
|
|
||||||
Text(title)
|
Text(title)
|
||||||
.font(.body)
|
.typography(.body)
|
||||||
.foregroundStyle(isDestructive ? Color.red : Color.Text.primary)
|
.foregroundStyle(isDestructive ? Color.red : Color.Text.primary)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|||||||
@ -40,21 +40,21 @@ struct ContactDetailView: View {
|
|||||||
VStack(alignment: .leading, spacing: Design.Spacing.large) {
|
VStack(alignment: .leading, spacing: Design.Spacing.large) {
|
||||||
// Name
|
// Name
|
||||||
Text(contact.name.isEmpty ? String.localized("Contact") : contact.name)
|
Text(contact.name.isEmpty ? String.localized("Contact") : contact.name)
|
||||||
.font(.largeTitle)
|
.typography(.hero)
|
||||||
.bold()
|
.bold()
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
|
|
||||||
// Connection details
|
// Connection details
|
||||||
VStack(alignment: .leading, spacing: Design.Spacing.small) {
|
VStack(alignment: .leading, spacing: Design.Spacing.small) {
|
||||||
Text("Connection details")
|
Text("Connection details")
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.Text.tertiary)
|
.foregroundStyle(Color.Text.tertiary)
|
||||||
|
|
||||||
HStack(spacing: Design.Spacing.small) {
|
HStack(spacing: Design.Spacing.small) {
|
||||||
Image(systemName: "calendar")
|
Image(systemName: "calendar")
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
Text(contact.lastSharedDate, format: .dateTime.day().month().year().hour().minute())
|
Text(contact.lastSharedDate, format: .dateTime.day().month().year().hour().minute())
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,7 +71,7 @@ struct ContactDetailView: View {
|
|||||||
showingAddTag = true
|
showingAddTag = true
|
||||||
} label: {
|
} label: {
|
||||||
Label(String.localized("Add tag"), systemImage: "plus")
|
Label(String.localized("Add tag"), systemImage: "plus")
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.bold()
|
.bold()
|
||||||
.foregroundStyle(Color.AppText.inverted)
|
.foregroundStyle(Color.AppText.inverted)
|
||||||
.padding(.horizontal, Design.Spacing.medium)
|
.padding(.horizontal, Design.Spacing.medium)
|
||||||
@ -87,7 +87,7 @@ struct ContactDetailView: View {
|
|||||||
// Notes section
|
// Notes section
|
||||||
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
|
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
|
||||||
Text("Notes")
|
Text("Notes")
|
||||||
.font(.headline)
|
.typography(.heading)
|
||||||
.bold()
|
.bold()
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ struct ContactDetailView: View {
|
|||||||
NotesEmptyState()
|
NotesEmptyState()
|
||||||
} else {
|
} else {
|
||||||
Text(contact.notes)
|
Text(contact.notes)
|
||||||
.font(.body)
|
.typography(.body)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.padding(Design.Spacing.medium)
|
.padding(Design.Spacing.medium)
|
||||||
@ -107,7 +107,7 @@ struct ContactDetailView: View {
|
|||||||
showingAddNote = true
|
showingAddNote = true
|
||||||
} label: {
|
} label: {
|
||||||
Label(String.localized("Add note"), systemImage: "plus")
|
Label(String.localized("Add note"), systemImage: "plus")
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
.padding(.vertical, Design.Spacing.medium)
|
.padding(.vertical, Design.Spacing.medium)
|
||||||
@ -314,9 +314,9 @@ private struct ContactBannerView: View {
|
|||||||
// Initials
|
// Initials
|
||||||
VStack(spacing: Design.Spacing.xxSmall) {
|
VStack(spacing: Design.Spacing.xxSmall) {
|
||||||
Text(String(initials.prefix(1)))
|
Text(String(initials.prefix(1)))
|
||||||
.font(.system(size: Design.BaseFontSize.display, weight: .light))
|
.typography(.title2)
|
||||||
Text(String(initials.dropFirst().prefix(1)))
|
Text(String(initials.dropFirst().prefix(1)))
|
||||||
.font(.system(size: Design.BaseFontSize.display, weight: .light))
|
.typography(.title2)
|
||||||
}
|
}
|
||||||
.foregroundStyle(Color.white.opacity(Design.Opacity.accent))
|
.foregroundStyle(Color.white.opacity(Design.Opacity.accent))
|
||||||
}
|
}
|
||||||
@ -327,7 +327,7 @@ private struct ContactBannerView: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
Button(action: onEditPhoto) {
|
Button(action: onEditPhoto) {
|
||||||
Image(systemName: contact.photoData == nil ? "camera.fill" : "pencil")
|
Image(systemName: contact.photoData == nil ? "camera.fill" : "pencil")
|
||||||
.font(.body)
|
.typography(.body)
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.padding(Design.Spacing.medium)
|
.padding(Design.Spacing.medium)
|
||||||
.background(.ultraThinMaterial)
|
.background(.ultraThinMaterial)
|
||||||
@ -354,12 +354,12 @@ private struct TagPill: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(spacing: Design.Spacing.xSmall) {
|
HStack(spacing: Design.Spacing.xSmall) {
|
||||||
Text(text)
|
Text(text)
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
Button {
|
Button {
|
||||||
onDelete()
|
onDelete()
|
||||||
} label: {
|
} label: {
|
||||||
Image(systemName: "xmark")
|
Image(systemName: "xmark")
|
||||||
.font(.caption2)
|
.typography(.caption2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
@ -461,7 +461,7 @@ private struct ContactFieldInfoRow: View {
|
|||||||
HStack(alignment: .top, spacing: Design.Spacing.medium) {
|
HStack(alignment: .top, spacing: Design.Spacing.medium) {
|
||||||
// Icon circle
|
// Icon circle
|
||||||
field.iconImage()
|
field.iconImage()
|
||||||
.font(.body)
|
.typography(.body)
|
||||||
.foregroundStyle(Color.white)
|
.foregroundStyle(Color.white)
|
||||||
.frame(width: Design.CardSize.avatarSize, height: Design.CardSize.avatarSize)
|
.frame(width: Design.CardSize.avatarSize, height: Design.CardSize.avatarSize)
|
||||||
.background(Color.CardPalette.coral)
|
.background(Color.CardPalette.coral)
|
||||||
@ -470,12 +470,12 @@ private struct ContactFieldInfoRow: View {
|
|||||||
// Text
|
// Text
|
||||||
VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) {
|
VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) {
|
||||||
Text(field.displayValue)
|
Text(field.displayValue)
|
||||||
.font(.body)
|
.typography(.body)
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
.multilineTextAlignment(.leading)
|
.multilineTextAlignment(.leading)
|
||||||
|
|
||||||
Text(field.title.isEmpty ? field.displayName : field.title)
|
Text(field.title.isEmpty ? field.displayName : field.title)
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(Color.Text.tertiary)
|
.foregroundStyle(Color.Text.tertiary)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -500,7 +500,7 @@ private struct ContactInfoRow: View {
|
|||||||
HStack(spacing: Design.Spacing.medium) {
|
HStack(spacing: Design.Spacing.medium) {
|
||||||
// Icon circle
|
// Icon circle
|
||||||
Image(systemName: icon)
|
Image(systemName: icon)
|
||||||
.font(.body)
|
.typography(.body)
|
||||||
.foregroundStyle(Color.white)
|
.foregroundStyle(Color.white)
|
||||||
.frame(width: Design.CardSize.avatarSize, height: Design.CardSize.avatarSize)
|
.frame(width: Design.CardSize.avatarSize, height: Design.CardSize.avatarSize)
|
||||||
.background(Color.CardPalette.coral)
|
.background(Color.CardPalette.coral)
|
||||||
@ -509,10 +509,10 @@ private struct ContactInfoRow: View {
|
|||||||
// Text
|
// Text
|
||||||
VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) {
|
VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) {
|
||||||
Text(value)
|
Text(value)
|
||||||
.font(.body)
|
.typography(.body)
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
Text(label)
|
Text(label)
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(Color.Text.tertiary)
|
.foregroundStyle(Color.Text.tertiary)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -530,11 +530,11 @@ private struct NotesEmptyState: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: Design.Spacing.medium) {
|
VStack(spacing: Design.Spacing.medium) {
|
||||||
Image(systemName: "note.text")
|
Image(systemName: "note.text")
|
||||||
.font(.system(size: Design.BaseFontSize.display))
|
.typography(.title2)
|
||||||
.foregroundStyle(Color.CardPalette.coral.opacity(Design.Opacity.medium))
|
.foregroundStyle(Color.CardPalette.coral.opacity(Design.Opacity.medium))
|
||||||
|
|
||||||
Text("Write down a memorable reminder about your contact")
|
Text("Write down a memorable reminder about your contact")
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.Text.tertiary)
|
.foregroundStyle(Color.Text.tertiary)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
}
|
}
|
||||||
@ -556,7 +556,7 @@ private struct BottomActionBar: View {
|
|||||||
HStack(spacing: Design.Spacing.medium) {
|
HStack(spacing: Design.Spacing.medium) {
|
||||||
Button(action: onMore) {
|
Button(action: onMore) {
|
||||||
Text("More...")
|
Text("More...")
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.bold()
|
.bold()
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
@ -567,7 +567,7 @@ private struct BottomActionBar: View {
|
|||||||
|
|
||||||
Button(action: onAddTag) {
|
Button(action: onAddTag) {
|
||||||
Text("Add tag")
|
Text("Add tag")
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.bold()
|
.bold()
|
||||||
.foregroundStyle(Color.AppText.inverted)
|
.foregroundStyle(Color.AppText.inverted)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
@ -578,7 +578,7 @@ private struct BottomActionBar: View {
|
|||||||
|
|
||||||
Button(action: onAddNote) {
|
Button(action: onAddNote) {
|
||||||
Text("Add note")
|
Text("Add note")
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.bold()
|
.bold()
|
||||||
.foregroundStyle(Color.AppText.inverted)
|
.foregroundStyle(Color.AppText.inverted)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
|
|||||||
@ -53,15 +53,15 @@ private struct EmptyContactsView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: Design.Spacing.large) {
|
VStack(spacing: Design.Spacing.large) {
|
||||||
Image(systemName: "person.2.slash")
|
Image(systemName: "person.2.slash")
|
||||||
.font(.system(size: Design.BaseFontSize.display))
|
.typography(.title2)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
|
|
||||||
Text("No contacts yet")
|
Text("No contacts yet")
|
||||||
.font(.headline)
|
.typography(.heading)
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
|
|
||||||
Text("Tap + to add a contact, scan a QR code, or track who you share your card with.")
|
Text("Tap + to add a contact, scan a QR code, or track who you share your card with.")
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.padding(.horizontal, Design.Spacing.xLarge)
|
.padding(.horizontal, Design.Spacing.xLarge)
|
||||||
@ -126,7 +126,7 @@ private struct ContactsListView: View {
|
|||||||
}
|
}
|
||||||
} header: {
|
} header: {
|
||||||
Text("Shared With")
|
Text("Shared With")
|
||||||
.font(.headline)
|
.typography(.heading)
|
||||||
.bold()
|
.bold()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -149,25 +149,25 @@ private struct ContactRowView: View {
|
|||||||
VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) {
|
VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) {
|
||||||
HStack(spacing: Design.Spacing.xSmall) {
|
HStack(spacing: Design.Spacing.xSmall) {
|
||||||
Text(contact.name)
|
Text(contact.name)
|
||||||
.font(.headline)
|
.typography(.heading)
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
|
|
||||||
if contact.isReceivedCard {
|
if contact.isReceivedCard {
|
||||||
Image(systemName: "arrow.down.circle.fill")
|
Image(systemName: "arrow.down.circle.fill")
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(Color.Accent.mint)
|
.foregroundStyle(Color.Accent.mint)
|
||||||
}
|
}
|
||||||
|
|
||||||
if contact.hasFollowUp {
|
if contact.hasFollowUp {
|
||||||
Image(systemName: contact.isFollowUpOverdue ? "exclamationmark.circle.fill" : "clock.fill")
|
Image(systemName: contact.isFollowUpOverdue ? "exclamationmark.circle.fill" : "clock.fill")
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(contact.isFollowUpOverdue ? Color.Accent.red : Color.Accent.gold)
|
.foregroundStyle(contact.isFollowUpOverdue ? Color.Accent.red : Color.Accent.gold)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !contact.role.isEmpty || !contact.company.isEmpty {
|
if !contact.role.isEmpty || !contact.company.isEmpty {
|
||||||
Text("\(contact.role)\(contact.role.isEmpty || contact.company.isEmpty ? "" : " · ")\(contact.company)")
|
Text("\(contact.role)\(contact.role.isEmpty || contact.company.isEmpty ? "" : " · ")\(contact.company)")
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
}
|
}
|
||||||
@ -176,7 +176,7 @@ private struct ContactRowView: View {
|
|||||||
HStack(spacing: Design.Spacing.xSmall) {
|
HStack(spacing: Design.Spacing.xSmall) {
|
||||||
ForEach(contact.tagList.prefix(2), id: \.self) { tag in
|
ForEach(contact.tagList.prefix(2), id: \.self) { tag in
|
||||||
Text(tag)
|
Text(tag)
|
||||||
.font(.caption2)
|
.typography(.caption2)
|
||||||
.padding(.horizontal, Design.Spacing.xSmall)
|
.padding(.horizontal, Design.Spacing.xSmall)
|
||||||
.padding(.vertical, Design.Spacing.xxSmall)
|
.padding(.vertical, Design.Spacing.xxSmall)
|
||||||
.background(Color.AppBackground.accent)
|
.background(Color.AppBackground.accent)
|
||||||
@ -190,10 +190,10 @@ private struct ContactRowView: View {
|
|||||||
|
|
||||||
VStack(alignment: .trailing, spacing: Design.Spacing.xxSmall) {
|
VStack(alignment: .trailing, spacing: Design.Spacing.xxSmall) {
|
||||||
Text(relativeDate)
|
Text(relativeDate)
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
Text(String.localized(contact.cardLabel))
|
Text(String.localized(contact.cardLabel))
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.padding(.horizontal, Design.Spacing.small)
|
.padding(.horizontal, Design.Spacing.small)
|
||||||
.padding(.vertical, Design.Spacing.xxSmall)
|
.padding(.vertical, Design.Spacing.xxSmall)
|
||||||
.background(Color.AppBackground.base)
|
.background(Color.AppBackground.base)
|
||||||
@ -218,7 +218,7 @@ private struct ContactAvatarView: View {
|
|||||||
.clipShape(.rect(cornerRadius: Design.CornerRadius.medium))
|
.clipShape(.rect(cornerRadius: Design.CornerRadius.medium))
|
||||||
} else {
|
} else {
|
||||||
Image(systemName: contact.avatarSystemName)
|
Image(systemName: contact.avatarSystemName)
|
||||||
.font(.title2)
|
.typography(.title2)
|
||||||
.foregroundStyle(Color.Accent.red)
|
.foregroundStyle(Color.Accent.red)
|
||||||
.frame(width: Design.CardSize.avatarSize, height: Design.CardSize.avatarSize)
|
.frame(width: Design.CardSize.avatarSize, height: Design.CardSize.avatarSize)
|
||||||
.background(Color.AppBackground.accent)
|
.background(Color.AppBackground.accent)
|
||||||
|
|||||||
@ -8,10 +8,10 @@ struct EmptyStateView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: Design.Spacing.small) {
|
VStack(spacing: Design.Spacing.small) {
|
||||||
Text(title)
|
Text(title)
|
||||||
.font(.headline)
|
.typography(.heading)
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
Text(message)
|
Text(message)
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -106,7 +106,7 @@ private struct ScannerOverlayView: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Text("Point at a QR code")
|
Text("Point at a QR code")
|
||||||
.font(.headline)
|
.typography(.heading)
|
||||||
.foregroundStyle(Color.Text.inverted)
|
.foregroundStyle(Color.Text.inverted)
|
||||||
.padding(Design.Spacing.medium)
|
.padding(Design.Spacing.medium)
|
||||||
.background(Color.black.opacity(Design.Opacity.medium))
|
.background(Color.black.opacity(Design.Opacity.medium))
|
||||||
@ -140,25 +140,25 @@ private struct ScannedResultView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: Design.Spacing.xLarge) {
|
VStack(spacing: Design.Spacing.xLarge) {
|
||||||
Image(systemName: isVCard ? "person.crop.circle.badge.checkmark" : "qrcode")
|
Image(systemName: isVCard ? "person.crop.circle.badge.checkmark" : "qrcode")
|
||||||
.font(.system(size: Design.BaseFontSize.display * 2))
|
.font(.system(size: Design.IconSize.xxxLarge))
|
||||||
.foregroundStyle(Color.Accent.red)
|
.foregroundStyle(Color.Accent.red)
|
||||||
|
|
||||||
if isVCard {
|
if isVCard {
|
||||||
VStack(spacing: Design.Spacing.small) {
|
VStack(spacing: Design.Spacing.small) {
|
||||||
Text("Card Found!")
|
Text("Card Found!")
|
||||||
.font(.title2)
|
.typography(.title2)
|
||||||
.bold()
|
.bold()
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
|
|
||||||
if let name = parsedName {
|
if let name = parsedName {
|
||||||
Text(name)
|
Text(name)
|
||||||
.font(.headline)
|
.typography(.heading)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Text("QR Code Scanned")
|
Text("QR Code Scanned")
|
||||||
.font(.title2)
|
.typography(.title2)
|
||||||
.bold()
|
.bold()
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
}
|
}
|
||||||
@ -172,7 +172,7 @@ private struct ScannedResultView: View {
|
|||||||
.controlSize(.large)
|
.controlSize(.large)
|
||||||
} else {
|
} else {
|
||||||
Text("This doesn't appear to be a business card QR code.")
|
Text("This doesn't appear to be a business card QR code.")
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.padding(.horizontal, Design.Spacing.xLarge)
|
.padding(.horizontal, Design.Spacing.xLarge)
|
||||||
|
|||||||
@ -50,7 +50,7 @@ private struct FloatingShareButton: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
Button(action: action) {
|
Button(action: action) {
|
||||||
Image(systemName: "qrcode")
|
Image(systemName: "qrcode")
|
||||||
.font(.title2)
|
.typography(.title2)
|
||||||
.fontWeight(.semibold)
|
.fontWeight(.semibold)
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.frame(width: Design.CardSize.floatingButtonSize, height: Design.CardSize.floatingButtonSize)
|
.frame(width: Design.CardSize.floatingButtonSize, height: Design.CardSize.floatingButtonSize)
|
||||||
|
|||||||
@ -2,13 +2,16 @@
|
|||||||
// SettingsView.swift
|
// SettingsView.swift
|
||||||
// BusinessCard
|
// BusinessCard
|
||||||
//
|
//
|
||||||
// App settings screen using Bedrock components.
|
// App settings screen using Bedrock settings layout contract:
|
||||||
|
// SettingsCard owns horizontal inset, custom rows use SettingsCardRow,
|
||||||
|
// and in-card separators use SettingsDivider.
|
||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Bedrock
|
import Bedrock
|
||||||
|
|
||||||
struct SettingsView: View {
|
struct SettingsView: View {
|
||||||
|
@Environment(AppState.self) private var appState
|
||||||
@State private var settingsState = SettingsState()
|
@State private var settingsState = SettingsState()
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
@ -17,7 +20,7 @@ struct SettingsView: View {
|
|||||||
VStack(spacing: Design.Spacing.large) {
|
VStack(spacing: Design.Spacing.large) {
|
||||||
|
|
||||||
// MARK: - About Section
|
// MARK: - About Section
|
||||||
|
appearanceSection
|
||||||
aboutSection
|
aboutSection
|
||||||
|
|
||||||
// MARK: - Debug Section
|
// MARK: - Debug Section
|
||||||
@ -31,12 +34,44 @@ struct SettingsView: View {
|
|||||||
.padding(.horizontal, Design.Spacing.large)
|
.padding(.horizontal, Design.Spacing.large)
|
||||||
.padding(.top, Design.Spacing.medium)
|
.padding(.top, Design.Spacing.medium)
|
||||||
}
|
}
|
||||||
.background(Color(.systemGroupedBackground))
|
.background(Color.AppBackground.base)
|
||||||
.navigationTitle(String.localized("Settings"))
|
.navigationTitle(String.localized("Settings"))
|
||||||
.navigationBarTitleDisplayMode(.large)
|
.navigationBarTitleDisplayMode(.large)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Appearance Section
|
||||||
|
|
||||||
|
private var appearanceSection: some View {
|
||||||
|
VStack(alignment: .leading, spacing: Design.Spacing.small) {
|
||||||
|
SettingsSectionHeader(
|
||||||
|
title: "Appearance",
|
||||||
|
systemImage: "paintbrush",
|
||||||
|
accentColor: AppThemeAccent.primary
|
||||||
|
)
|
||||||
|
|
||||||
|
SettingsCard(
|
||||||
|
backgroundColor: Color.AppBackground.secondary,
|
||||||
|
borderColor: .clear
|
||||||
|
) {
|
||||||
|
SettingsSegmentedPicker(
|
||||||
|
title: "Theme",
|
||||||
|
subtitle: "Choose app theme",
|
||||||
|
options: [
|
||||||
|
("System", AppAppearance.system),
|
||||||
|
("Light", AppAppearance.light),
|
||||||
|
("Dark", AppAppearance.dark)
|
||||||
|
],
|
||||||
|
selection: Binding(
|
||||||
|
get: { appState.appearance },
|
||||||
|
set: { appState.appearance = $0 }
|
||||||
|
),
|
||||||
|
accentColor: AppThemeAccent.primary
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - About Section
|
// MARK: - About Section
|
||||||
|
|
||||||
private var aboutSection: some View {
|
private var aboutSection: some View {
|
||||||
@ -48,43 +83,49 @@ struct SettingsView: View {
|
|||||||
)
|
)
|
||||||
|
|
||||||
SettingsCard(
|
SettingsCard(
|
||||||
backgroundColor: Color(.secondarySystemGroupedBackground),
|
backgroundColor: Color.AppBackground.secondary,
|
||||||
borderColor: .clear
|
borderColor: .clear
|
||||||
) {
|
) {
|
||||||
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
|
SettingsCardRow {
|
||||||
// App Name
|
|
||||||
HStack {
|
HStack {
|
||||||
Text(settingsState.appName)
|
Text(settingsState.appName)
|
||||||
.font(.system(size: Design.BaseFontSize.title, weight: .semibold))
|
.typography(.title3Bold)
|
||||||
.foregroundStyle(.primary)
|
.foregroundStyle(Color.AppText.primary)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Version
|
SettingsDivider(color: AppBorder.subtle)
|
||||||
|
|
||||||
|
SettingsCardRow {
|
||||||
HStack {
|
HStack {
|
||||||
Text(String.localized("Version"))
|
Text(String.localized("Version"))
|
||||||
.font(.system(size: Design.BaseFontSize.body, weight: .medium))
|
.typography(.bodyEmphasis)
|
||||||
.foregroundStyle(.primary)
|
.foregroundStyle(Color.AppText.primary)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Text(settingsState.versionString)
|
Text(settingsState.versionString)
|
||||||
.font(.system(size: Design.BaseFontSize.body, design: .monospaced))
|
.typography(.body)
|
||||||
.foregroundStyle(.secondary)
|
.fontDesign(.monospaced)
|
||||||
|
.foregroundStyle(Color.AppText.secondary)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Developer
|
SettingsDivider(color: AppBorder.subtle)
|
||||||
|
|
||||||
|
SettingsCardRow {
|
||||||
HStack {
|
HStack {
|
||||||
Text(String.localized("Developer"))
|
Text(String.localized("Developer"))
|
||||||
.font(.system(size: Design.BaseFontSize.body, weight: .medium))
|
.typography(.bodyEmphasis)
|
||||||
.foregroundStyle(.primary)
|
.foregroundStyle(Color.AppText.primary)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Text("Matt Bruce")
|
Text("Matt Bruce")
|
||||||
.font(.system(size: Design.BaseFontSize.body))
|
.typography(.body)
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(Color.AppText.secondary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,10 +144,9 @@ struct SettingsView: View {
|
|||||||
)
|
)
|
||||||
|
|
||||||
SettingsCard(
|
SettingsCard(
|
||||||
backgroundColor: Color(.secondarySystemGroupedBackground),
|
backgroundColor: Color.AppBackground.secondary,
|
||||||
borderColor: .clear
|
borderColor: .clear
|
||||||
) {
|
) {
|
||||||
VStack(spacing: Design.Spacing.medium) {
|
|
||||||
SettingsToggle(
|
SettingsToggle(
|
||||||
title: "Enable Debug Premium",
|
title: "Enable Debug Premium",
|
||||||
subtitle: "Unlock all premium features for testing",
|
subtitle: "Unlock all premium features for testing",
|
||||||
@ -117,18 +157,22 @@ struct SettingsView: View {
|
|||||||
accentColor: AppStatus.warning
|
accentColor: AppStatus.warning
|
||||||
)
|
)
|
||||||
|
|
||||||
|
SettingsDivider(color: AppBorder.subtle)
|
||||||
|
|
||||||
SettingsNavigationRow(
|
SettingsNavigationRow(
|
||||||
title: "Icon Generator",
|
title: "Icon Generator",
|
||||||
subtitle: "Generate and save app icon to Files",
|
subtitle: "Generate and save app icon to Files",
|
||||||
backgroundColor: Color(.tertiarySystemGroupedBackground)
|
backgroundColor: .clear
|
||||||
) {
|
) {
|
||||||
IconGeneratorView(config: .businessCard, appName: "BusinessCard")
|
IconGeneratorView(config: .businessCard, appName: "BusinessCard")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SettingsDivider(color: AppBorder.subtle)
|
||||||
|
|
||||||
SettingsNavigationRow(
|
SettingsNavigationRow(
|
||||||
title: "Branding Preview",
|
title: "Branding Preview",
|
||||||
subtitle: "Preview app icon and launch screen",
|
subtitle: "Preview app icon and launch screen",
|
||||||
backgroundColor: Color(.tertiarySystemGroupedBackground)
|
backgroundColor: .clear
|
||||||
) {
|
) {
|
||||||
BrandingPreviewView(
|
BrandingPreviewView(
|
||||||
iconConfig: .businessCard,
|
iconConfig: .businessCard,
|
||||||
@ -139,7 +183,6 @@ struct SettingsView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -105,7 +105,7 @@ private struct QRCodeSection: View {
|
|||||||
|
|
||||||
// Instruction text
|
// Instruction text
|
||||||
Text("Point your camera at the QR code to receive the card")
|
Text("Point your camera at the QR code to receive the card")
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.ShareSheet.secondaryText)
|
.foregroundStyle(Color.ShareSheet.secondaryText)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
}
|
}
|
||||||
@ -127,10 +127,10 @@ private struct AppClipSection: View {
|
|||||||
// Header
|
// Header
|
||||||
HStack {
|
HStack {
|
||||||
Image(systemName: "app.gift")
|
Image(systemName: "app.gift")
|
||||||
.font(.headline)
|
.typography(.heading)
|
||||||
.foregroundStyle(Color.ShareSheet.text)
|
.foregroundStyle(Color.ShareSheet.text)
|
||||||
Text("App Clip (includes photo)")
|
Text("App Clip (includes photo)")
|
||||||
.font(.headline)
|
.typography(.heading)
|
||||||
.foregroundStyle(Color.ShareSheet.text)
|
.foregroundStyle(Color.ShareSheet.text)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,7 +150,7 @@ private struct AppClipSection: View {
|
|||||||
|
|
||||||
// Expiration notice
|
// Expiration notice
|
||||||
Text("Expires in 7 days")
|
Text("Expires in 7 days")
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(Color.ShareSheet.secondaryText)
|
.foregroundStyle(Color.ShareSheet.secondaryText)
|
||||||
|
|
||||||
// Reset button
|
// Reset button
|
||||||
@ -158,7 +158,7 @@ private struct AppClipSection: View {
|
|||||||
appClipState.reset()
|
appClipState.reset()
|
||||||
} label: {
|
} label: {
|
||||||
Text("Generate New Link")
|
Text("Generate New Link")
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.ShareSheet.text)
|
.foregroundStyle(Color.ShareSheet.text)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -170,7 +170,7 @@ private struct AppClipSection: View {
|
|||||||
Image(systemName: "qrcode")
|
Image(systemName: "qrcode")
|
||||||
Text("Generate App Clip Link")
|
Text("Generate App Clip Link")
|
||||||
}
|
}
|
||||||
.font(.headline)
|
.typography(.heading)
|
||||||
.foregroundStyle(Color.ShareSheet.background)
|
.foregroundStyle(Color.ShareSheet.background)
|
||||||
.padding(.horizontal, Design.Spacing.xLarge)
|
.padding(.horizontal, Design.Spacing.xLarge)
|
||||||
.padding(.vertical, Design.Spacing.medium)
|
.padding(.vertical, Design.Spacing.medium)
|
||||||
@ -180,7 +180,7 @@ private struct AppClipSection: View {
|
|||||||
|
|
||||||
// Description
|
// Description
|
||||||
Text("Creates a link that opens a mini-app for recipients to preview and save your card with photo.")
|
Text("Creates a link that opens a mini-app for recipients to preview and save your card with photo.")
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(Color.ShareSheet.secondaryText)
|
.foregroundStyle(Color.ShareSheet.secondaryText)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
}
|
}
|
||||||
@ -188,7 +188,7 @@ private struct AppClipSection: View {
|
|||||||
// Error message
|
// Error message
|
||||||
if let error = appClipState.errorMessage {
|
if let error = appClipState.errorMessage {
|
||||||
Text(error)
|
Text(error)
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(.red)
|
.foregroundStyle(.red)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
}
|
}
|
||||||
@ -279,15 +279,15 @@ private struct EmptyShareState: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: Design.Spacing.large) {
|
VStack(spacing: Design.Spacing.large) {
|
||||||
Image(systemName: "rectangle.on.rectangle.slash")
|
Image(systemName: "rectangle.on.rectangle.slash")
|
||||||
.font(.system(size: Design.BaseFontSize.display))
|
.typography(.title2)
|
||||||
.foregroundStyle(Color.ShareSheet.secondaryText)
|
.foregroundStyle(Color.ShareSheet.secondaryText)
|
||||||
|
|
||||||
Text("No card selected")
|
Text("No card selected")
|
||||||
.font(.headline)
|
.typography(.heading)
|
||||||
.foregroundStyle(Color.ShareSheet.text)
|
.foregroundStyle(Color.ShareSheet.text)
|
||||||
|
|
||||||
Text("Choose a card in the My Cards tab to start sharing.")
|
Text("Choose a card in the My Cards tab to start sharing.")
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.ShareSheet.secondaryText)
|
.foregroundStyle(Color.ShareSheet.secondaryText)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
}
|
}
|
||||||
@ -305,7 +305,7 @@ private struct RowContent: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(spacing: Design.Spacing.medium) {
|
HStack(spacing: Design.Spacing.medium) {
|
||||||
Image(systemName: systemImage)
|
Image(systemName: systemImage)
|
||||||
.font(.body)
|
.typography(.body)
|
||||||
.foregroundStyle(iconColor)
|
.foregroundStyle(iconColor)
|
||||||
.frame(width: Design.Spacing.xLarge)
|
.frame(width: Design.Spacing.xLarge)
|
||||||
|
|
||||||
|
|||||||
@ -280,7 +280,7 @@ private struct ContactPhotoRow: View {
|
|||||||
.scaledToFill()
|
.scaledToFill()
|
||||||
} else {
|
} else {
|
||||||
Image(systemName: "person.crop.circle.fill")
|
Image(systemName: "person.crop.circle.fill")
|
||||||
.font(.system(size: Design.BaseFontSize.display))
|
.typography(.title2)
|
||||||
.foregroundStyle(Color.Text.tertiary)
|
.foregroundStyle(Color.Text.tertiary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -290,18 +290,18 @@ private struct ContactPhotoRow: View {
|
|||||||
|
|
||||||
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
|
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
|
||||||
Text("Profile Photo")
|
Text("Profile Photo")
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
|
|
||||||
Text(photoData == nil ? String.localized("Add a photo") : String.localized("Tap to change"))
|
Text(photoData == nil ? String.localized("Add a photo") : String.localized("Tap to change"))
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Image(systemName: "chevron.right")
|
Image(systemName: "chevron.right")
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(Color.Text.tertiary)
|
.foregroundStyle(Color.Text.tertiary)
|
||||||
}
|
}
|
||||||
.padding(.vertical, Design.Spacing.xSmall)
|
.padding(.vertical, Design.Spacing.xSmall)
|
||||||
@ -342,7 +342,7 @@ private struct LabeledFieldRow: View {
|
|||||||
Text(entry.label)
|
Text(entry.label)
|
||||||
.foregroundStyle(Color.accentColor)
|
.foregroundStyle(Color.accentColor)
|
||||||
Image(systemName: "chevron.up.chevron.down")
|
Image(systemName: "chevron.up.chevron.down")
|
||||||
.font(.caption2)
|
.typography(.caption2)
|
||||||
.foregroundStyle(Color.secondary)
|
.foregroundStyle(Color.secondary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -76,7 +76,7 @@ struct ContactFieldEditorSheet: View {
|
|||||||
} else {
|
} else {
|
||||||
VStack(alignment: .leading, spacing: Design.Spacing.small) {
|
VStack(alignment: .leading, spacing: Design.Spacing.small) {
|
||||||
Text(fieldType.valueLabel)
|
Text(fieldType.valueLabel)
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
|
|
||||||
TextField(fieldType.valuePlaceholder, text: $value)
|
TextField(fieldType.valuePlaceholder, text: $value)
|
||||||
@ -91,7 +91,7 @@ struct ContactFieldEditorSheet: View {
|
|||||||
// Title field
|
// Title field
|
||||||
VStack(alignment: .leading, spacing: Design.Spacing.small) {
|
VStack(alignment: .leading, spacing: Design.Spacing.small) {
|
||||||
Text("Title (optional)")
|
Text("Title (optional)")
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
|
|
||||||
TextField(String(localized: "e.g. Work, Personal"), text: $title)
|
TextField(String(localized: "e.g. Work, Personal"), text: $title)
|
||||||
@ -102,7 +102,7 @@ struct ContactFieldEditorSheet: View {
|
|||||||
if !fieldType.titleSuggestions.isEmpty {
|
if !fieldType.titleSuggestions.isEmpty {
|
||||||
VStack(alignment: .leading, spacing: Design.Spacing.small) {
|
VStack(alignment: .leading, spacing: Design.Spacing.small) {
|
||||||
Text("Here are some suggestions for your title:")
|
Text("Here are some suggestions for your title:")
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
|
|
||||||
FlowLayout(spacing: Design.Spacing.small) {
|
FlowLayout(spacing: Design.Spacing.small) {
|
||||||
@ -191,12 +191,12 @@ private struct FieldHeaderView: View {
|
|||||||
.frame(width: Design.CardSize.avatarSize, height: Design.CardSize.avatarSize)
|
.frame(width: Design.CardSize.avatarSize, height: Design.CardSize.avatarSize)
|
||||||
.overlay(
|
.overlay(
|
||||||
fieldType.iconImage()
|
fieldType.iconImage()
|
||||||
.font(.title3)
|
.typography(.title3)
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
)
|
)
|
||||||
|
|
||||||
Text(fieldType.displayName)
|
Text(fieldType.displayName)
|
||||||
.font(.headline)
|
.typography(.heading)
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
@ -215,7 +215,7 @@ private struct SuggestionChip: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
Button(action: action) {
|
Button(action: action) {
|
||||||
Text(text)
|
Text(text)
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.padding(.horizontal, Design.Spacing.medium)
|
.padding(.horizontal, Design.Spacing.medium)
|
||||||
.padding(.vertical, Design.Spacing.small)
|
.padding(.vertical, Design.Spacing.small)
|
||||||
.background(Color.AppBackground.elevated)
|
.background(Color.AppBackground.elevated)
|
||||||
|
|||||||
@ -47,7 +47,7 @@ struct LogoEditorSheet: View {
|
|||||||
onComplete(nil)
|
onComplete(nil)
|
||||||
} label: {
|
} label: {
|
||||||
Image(systemName: "xmark")
|
Image(systemName: "xmark")
|
||||||
.font(.body.bold())
|
.typography(.bodyEmphasis)
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -106,19 +106,19 @@ struct LogoEditorSheet: View {
|
|||||||
private var zoomSliderSection: some View {
|
private var zoomSliderSection: some View {
|
||||||
VStack(alignment: .leading, spacing: Design.Spacing.small) {
|
VStack(alignment: .leading, spacing: Design.Spacing.small) {
|
||||||
Text("Zoom in/out")
|
Text("Zoom in/out")
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
|
|
||||||
HStack(spacing: Design.Spacing.medium) {
|
HStack(spacing: Design.Spacing.medium) {
|
||||||
Image(systemName: "minus.magnifyingglass")
|
Image(systemName: "minus.magnifyingglass")
|
||||||
.font(.body)
|
.typography(.body)
|
||||||
.foregroundStyle(Color.Text.tertiary)
|
.foregroundStyle(Color.Text.tertiary)
|
||||||
|
|
||||||
Slider(value: $zoomScale, in: minZoom...maxZoom)
|
Slider(value: $zoomScale, in: minZoom...maxZoom)
|
||||||
.tint(Color.accentColor)
|
.tint(Color.accentColor)
|
||||||
|
|
||||||
Image(systemName: "plus.magnifyingglass")
|
Image(systemName: "plus.magnifyingglass")
|
||||||
.font(.body)
|
.typography(.body)
|
||||||
.foregroundStyle(Color.Text.tertiary)
|
.foregroundStyle(Color.Text.tertiary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -129,13 +129,13 @@ struct LogoEditorSheet: View {
|
|||||||
private var backgroundColorSection: some View {
|
private var backgroundColorSection: some View {
|
||||||
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
|
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
|
||||||
Text("Change background color")
|
Text("Change background color")
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
|
|
||||||
HStack(spacing: Design.Spacing.large) {
|
HStack(spacing: Design.Spacing.large) {
|
||||||
// Suggested label and colors
|
// Suggested label and colors
|
||||||
Text("Suggested")
|
Text("Suggested")
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
|
|
||||||
HStack(spacing: Design.Spacing.small) {
|
HStack(spacing: Design.Spacing.small) {
|
||||||
@ -158,7 +158,7 @@ struct LogoEditorSheet: View {
|
|||||||
} label: {
|
} label: {
|
||||||
HStack(spacing: Design.Spacing.small) {
|
HStack(spacing: Design.Spacing.small) {
|
||||||
Text("Custom color")
|
Text("Custom color")
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
|
|
||||||
ColorSwatchButton(
|
ColorSwatchButton(
|
||||||
@ -171,7 +171,7 @@ struct LogoEditorSheet: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Image(systemName: "chevron.right")
|
Image(systemName: "chevron.right")
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(Color.Text.tertiary)
|
.foregroundStyle(Color.Text.tertiary)
|
||||||
}
|
}
|
||||||
.contentShape(.rect)
|
.contentShape(.rect)
|
||||||
|
|||||||
@ -29,7 +29,7 @@ struct RecordContactSheet: View {
|
|||||||
|
|
||||||
Section {
|
Section {
|
||||||
Text("This person will appear in your Contacts tab so you can track who has your card.")
|
Text("This person will appear in your Contacts tab so you can track who has your card.")
|
||||||
.font(.footnote)
|
.typography(.footnote)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ struct WidgetsView: View {
|
|||||||
ScrollView {
|
ScrollView {
|
||||||
VStack(spacing: Design.Spacing.large) {
|
VStack(spacing: Design.Spacing.large) {
|
||||||
Text("Share using widgets on your phone or watch")
|
Text("Share using widgets on your phone or watch")
|
||||||
.font(.title2)
|
.typography(.title2)
|
||||||
.bold()
|
.bold()
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ private struct PhoneWidgetPreview: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
|
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
|
||||||
Text("Phone Widget")
|
Text("Phone Widget")
|
||||||
.font(.headline)
|
.typography(.heading)
|
||||||
.bold()
|
.bold()
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
|
|
||||||
@ -54,13 +54,13 @@ private struct PhoneWidgetPreview: View {
|
|||||||
|
|
||||||
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
|
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
|
||||||
Text(card.fullName)
|
Text(card.fullName)
|
||||||
.font(.headline)
|
.typography(.heading)
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
Text(card.role)
|
Text(card.role)
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
Text("Tap to share")
|
Text("Tap to share")
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -77,7 +77,7 @@ private struct WatchWidgetPreview: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
|
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
|
||||||
Text("Watch Widget")
|
Text("Watch Widget")
|
||||||
.font(.headline)
|
.typography(.heading)
|
||||||
.bold()
|
.bold()
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
|
|
||||||
@ -87,10 +87,10 @@ private struct WatchWidgetPreview: View {
|
|||||||
|
|
||||||
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
|
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
|
||||||
Text("Ready to scan")
|
Text("Ready to scan")
|
||||||
.font(.subheadline)
|
.typography(.subheading)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
Text("Open on Apple Watch")
|
Text("Open on Apple Watch")
|
||||||
.font(.caption)
|
.typography(.caption)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -141,6 +141,15 @@ The app uses the [Bedrock](ssh://git@192.168.1.128:220/mbrucedogs/Bedrock.git) p
|
|||||||
|
|
||||||
App-specific extensions are in `Design/DesignConstants.swift`.
|
App-specific extensions are in `Design/DesignConstants.swift`.
|
||||||
|
|
||||||
|
### Settings Layout Contract
|
||||||
|
|
||||||
|
`SettingsCard` is the single owner of horizontal row inset in `SettingsView`.
|
||||||
|
|
||||||
|
- Use `SettingsCardRow` for custom in-card rows (`HStack`, status/info rows, custom content).
|
||||||
|
- Use `SettingsDivider` between in-card rows (instead of `Divider`/manual lines).
|
||||||
|
- Use `SettingsNavigationRow(..., backgroundColor: .clear)` for standard in-card navigation rows.
|
||||||
|
- Avoid child `.padding(.horizontal, ...)` inside `SettingsCard` unless intentional indentation is required.
|
||||||
|
|
||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user