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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Demo/.figmagen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,8 @@ tokens:
templateOptions:
publicAccess: true
shadowTypeName: ShadowToken
theme:
destination: FigmaGenDemo/Generated/Theme.swift
templateOptions:
publicAccess: true
shadowTypeName: ShadowToken
4 changes: 4 additions & 0 deletions Demo/FigmaGenDemo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
8E5334162A420D9B006D6569 /* SF-Pro-Display-Semibold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 8E5334062A420D9B006D6569 /* SF-Pro-Display-Semibold.otf */; };
8E5334172A420D9B006D6569 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E5334072A420D9B006D6569 /* AppDelegate.swift */; };
8E53341C2A420DA4006D6569 /* FigmaGenDemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E53341A2A420DA4006D6569 /* FigmaGenDemoTests.swift */; };
8E97DB762A741FD600E8AAC1 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E97DB752A741FD600E8AAC1 /* Theme.swift */; };
8EAE283E2A716D4A007C477F /* BoxShadowTokens.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EAE283D2A716D4A007C477F /* BoxShadowTokens.swift */; };
8EFF17F52A70096E00C47577 /* FontFamilyTokens.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EFF17F32A70096D00C47577 /* FontFamilyTokens.swift */; };
8EFF17F62A70096E00C47577 /* TypographyTokens.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EFF17F42A70096D00C47577 /* TypographyTokens.swift */; };
Expand Down Expand Up @@ -82,6 +83,7 @@
8E5334082A420D9B006D6569 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
8E53341A2A420DA4006D6569 /* FigmaGenDemoTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FigmaGenDemoTests.swift; sourceTree = "<group>"; };
8E53341B2A420DA4006D6569 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
8E97DB752A741FD600E8AAC1 /* Theme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = "<group>"; };
8EAE283D2A716D4A007C477F /* BoxShadowTokens.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoxShadowTokens.swift; sourceTree = "<group>"; };
8EFF17F32A70096D00C47577 /* FontFamilyTokens.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FontFamilyTokens.swift; sourceTree = "<group>"; };
8EFF17F42A70096D00C47577 /* TypographyTokens.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TypographyTokens.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -152,6 +154,7 @@
8E5333F62A420D9B006D6569 /* Images.swift */,
8E5333F42A420D9B006D6569 /* ShadowStyle.swift */,
8E5333F52A420D9B006D6569 /* TextStyle.swift */,
8E97DB752A741FD600E8AAC1 /* Theme.swift */,
8EFF17F42A70096D00C47577 /* TypographyTokens.swift */,
);
path = Generated;
Expand Down Expand Up @@ -403,6 +406,7 @@
8E53340A2A420D9B006D6569 /* TextStyle.swift in Sources */,
8E53340B2A420D9B006D6569 /* Images.swift in Sources */,
8E5334092A420D9B006D6569 /* ShadowStyle.swift in Sources */,
8E97DB762A741FD600E8AAC1 /* Theme.swift in Sources */,
8E53340C2A420D9B006D6569 /* ColorStyle.swift in Sources */,
8E44BFD52A67ECB300EE5D7E /* ColorTokens.swift in Sources */,
);
Expand Down
131 changes: 131 additions & 0 deletions Demo/FigmaGenDemo/Generated/Theme.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// swiftlint:disable all
// Generated using FigmaGen - https://github.com/hhru/FigmaGen

#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif

public struct Theme {

public let colors: ColorTokens
public let shadows: BoxShadowTokens
public let typographies: TypographyTokens

init(
colors: ColorTokens,
shadows: BoxShadowTokens,
typographies: TypographyTokens = TypographyTokens()
) {
self.colors = colors
self.shadows = shadows
self.typographies = typographies
}
}

extension Theme {

public static let defaultLight = Self(
colors: ColorTokens(
accent: ColorTokens.Accent(
bg: UIColor(hex: 0xC3DAFEFF),
default: UIColor(hex: 0x7F9CF5FF),
onAccent: UIColor(hex: 0xFFFFFFFF)
),
bg: ColorTokens.Bg(
default: UIColor(hex: 0xFFFFFFFF),
muted: UIColor(hex: 0xF7FAFCFF),
subtle: UIColor(hex: 0xEDF2F7FF)
),
fg: ColorTokens.Fg(
default: UIColor(hex: 0x000000FF),
muted: UIColor(hex: 0x4A5568FF),
subtle: UIColor(hex: 0xA0AEC0FF)
),
shadows: ColorTokens.Shadows(
default: UIColor(hex: 0x1A202CFF)
)
),
shadows: BoxShadowTokens(
level1: ShadowToken(
offset: CGSize(width: 0, height: 4),
radius: 12,
color: UIColor(hex: 0x7090B029),
opacity: 1.0
),
level2: ShadowToken(
offset: CGSize(width: 0, height: 8),
radius: 16,
color: UIColor(hex: 0x7090B03D),
opacity: 1.0
),
level3: ShadowToken(
offset: CGSize(width: 0, height: 12),
radius: 24,
color: UIColor(hex: 0x7090B052),
opacity: 1.0
)
)
)

public static let defaultDark = Self(
colors: ColorTokens(
accent: ColorTokens.Accent(
bg: UIColor(hex: 0x434190FF),
default: UIColor(hex: 0x5A67D8FF),
onAccent: UIColor(hex: 0xFFFFFFFF)
),
bg: ColorTokens.Bg(
default: UIColor(hex: 0x1A202CFF),
muted: UIColor(hex: 0x4A5568FF),
subtle: UIColor(hex: 0x718096FF)
),
fg: ColorTokens.Fg(
default: UIColor(hex: 0xFFFFFFFF),
muted: UIColor(hex: 0xE2E8F0FF),
subtle: UIColor(hex: 0xA0AEC0FF)
),
shadows: ColorTokens.Shadows(
default: UIColor(hex: 0x00000000)
)
),
shadows: BoxShadowTokens(
level1: ShadowToken(
offset: CGSize(width: 0, height: 4),
radius: 12,
color: UIColor(hex: 0x7090B029),
opacity: 1.0
),
level2: ShadowToken(
offset: CGSize(width: 0, height: 8),
radius: 16,
color: UIColor(hex: 0x7090B03D),
opacity: 1.0
),
level3: ShadowToken(
offset: CGSize(width: 0, height: 12),
radius: 24,
color: UIColor(hex: 0x7090B052),
opacity: 1.0
)
)
)
}

private extension UIColor {

convenience init(hex: UInt32) {
let red = UInt8((hex >> 24) & 0xFF)
let green = UInt8((hex >> 16) & 0xFF)
let blue = UInt8((hex >> 8) & 0xFF)
let alpha = UInt8(hex & 0xFF)

self.init(
red: CGFloat(red) / 255.0,
green: CGFloat(green) / 255.0,
blue: CGFloat(blue) / 255.0,
alpha: CGFloat(alpha) / 255.0
)
}
}
29 changes: 29 additions & 0 deletions Sources/FigmaGen/Commands/TokensCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,30 @@ final class TokensCommand: AsyncExecutableCommand {
"""
)

let themeTemplate = Key<String>(
"--theme-template",
description: """
Path to the template file.
If no template is passed a default template will be used.
"""
)

let themeTemplateOptions = VariadicKey<String>(
"--theme-options",
description: #"""
An option that will be merged with template context, and overwrite any values of the same name.
Can be repeated multiple times and must be in the format: -o "name:value".
"""#
)

let themeDestination = Key<String>(
"--theme-destination",
description: """
The path to the file to generate.
By default, generated code will be printed on stdout.
"""
)

// MARK: - Initializers

init(generator: TokensGenerator) {
Expand Down Expand Up @@ -204,6 +228,11 @@ extension TokensCommand {
template: boxShadowsTemplate.value,
templateOptions: resolveTemplateOptions(boxShadowsTemplateOptions.value),
destination: boxShadowsDestination.value
),
theme: TokensTemplateConfiguration.Template(
template: themeTemplate.value,
templateOptions: resolveTemplateOptions(themeTemplateOptions.value),
destination: themeDestination.value
)
)
)
Expand Down
21 changes: 17 additions & 4 deletions Sources/FigmaGen/Dependencies.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ enum Dependencies {
static let tokensGenerationParametersResolver: TokensGenerationParametersResolver
= DefaultTokensGenerationParametersResolver()

static let colorTokensContextProvider: ColorTokensContextProvider = DefaultColorTokensContextProvider(
tokensResolver: tokensResolver
)

static let boxShadowTokensContextProvider: BoxShadowTokensContextProvider = DefaultBoxShadowTokensContextProvider()

// MARK: -

static let templateContextCoder: TemplateContextCoder = DefaultTemplateContextCoder()
Expand Down Expand Up @@ -120,8 +126,8 @@ enum Dependencies {
)

static let colorTokensGenerator: ColorTokensGenerator = DefaultColorTokensGenerator(
tokensResolver: tokensResolver,
templateRenderer: templateRenderer
templateRenderer: templateRenderer,
colorTokensContextProvider: colorTokensContextProvider
)

static let baseColorTokensGenerator: BaseColorTokensGenerator = DefaultBaseColorTokensGenerator(
Expand All @@ -140,7 +146,13 @@ enum Dependencies {
)

static let boxShadowTokensGenerator: BoxShadowTokensGenerator = DefaultBoxShadowTokensGenerator(
tokensResolver: tokensResolver,
boxShadowTokensContextProvider: boxShadowTokensContextProvider,
templateRenderer: templateRenderer
)

static let themeTokensGenerator: ThemeTokensGenerator = DefaultThemeTokensGenerator(
colorTokensContextProvider: colorTokensContextProvider,
boxShadowsContextProvider: boxShadowTokensContextProvider,
templateRenderer: templateRenderer
)

Expand All @@ -151,7 +163,8 @@ enum Dependencies {
baseColorTokensGenerator: baseColorTokensGenerator,
fontFamilyTokensGenerator: fontFamilyTokensGenerator,
typographyTokensGenerator: typographyTokensGenerator,
boxShadowTokensGenerator: boxShadowTokensGenerator
boxShadowTokensGenerator: boxShadowTokensGenerator,
themeTokensGenerator: themeTokensGenerator
)

static let libraryGenerator: LibraryGenerator = DefaultLibraryGenerator(
Expand Down
10 changes: 9 additions & 1 deletion Sources/FigmaGen/Generators/Tokens/DefaultTokensGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ final class DefaultTokensGenerator: TokensGenerator {
let fontFamilyTokensGenerator: FontFamilyTokensGenerator
let typographyTokensGenerator: TypographyTokensGenerator
let boxShadowTokensGenerator: BoxShadowTokensGenerator
let themeTokensGenerator: ThemeTokensGenerator

// MARK: - Initializers

Expand All @@ -23,7 +24,8 @@ final class DefaultTokensGenerator: TokensGenerator {
baseColorTokensGenerator: BaseColorTokensGenerator,
fontFamilyTokensGenerator: FontFamilyTokensGenerator,
typographyTokensGenerator: TypographyTokensGenerator,
boxShadowTokensGenerator: BoxShadowTokensGenerator
boxShadowTokensGenerator: BoxShadowTokensGenerator,
themeTokensGenerator: ThemeTokensGenerator
) {
self.tokensProvider = tokensProvider
self.tokensGenerationParametersResolver = tokensGenerationParametersResolver
Expand All @@ -32,6 +34,7 @@ final class DefaultTokensGenerator: TokensGenerator {
self.fontFamilyTokensGenerator = fontFamilyTokensGenerator
self.typographyTokensGenerator = typographyTokensGenerator
self.boxShadowTokensGenerator = boxShadowTokensGenerator
self.themeTokensGenerator = themeTokensGenerator
}

// MARK: - Instance Methods
Expand Down Expand Up @@ -63,6 +66,11 @@ final class DefaultTokensGenerator: TokensGenerator {
renderParameters: parameters.tokens.boxShadowRender,
tokenValues: tokenValues
)

try themeTokensGenerator.generate(
renderParameters: parameters.tokens.themeRender,
tokenValues: tokenValues
)
}

// MARK: -
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,20 @@ final class DefaultTokensGenerationParametersResolver: TokensGenerationParameter
nativeTemplateName: "BoxShadowTokens"
)

let themeRender = resolveRenderParameters(
template: configuration.templates?.theme,
nativeTemplateName: "Theme"
)

return TokensGenerationParameters(
file: file,
tokens: TokensGenerationParameters.TokensParameters(
colorRender: colorRender,
baseColorRender: baseColorRender,
fontFamilyRender: fontFamilyRender,
typographyRender: typographyRender,
boxShadowRender: boxShadowRender
boxShadowRender: boxShadowRender,
themeRender: themeRender
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,58 +4,20 @@ final class DefaultBoxShadowTokensGenerator: BoxShadowTokensGenerator {

// MARK: - Instance Properties

let tokensResolver: TokensResolver
let boxShadowTokensContextProvider: BoxShadowTokensContextProvider
let templateRenderer: TemplateRenderer

// MARK: - Initializers

init(tokensResolver: TokensResolver, templateRenderer: TemplateRenderer) {
self.tokensResolver = tokensResolver
init(boxShadowTokensContextProvider: BoxShadowTokensContextProvider, templateRenderer: TemplateRenderer) {
self.boxShadowTokensContextProvider = boxShadowTokensContextProvider
self.templateRenderer = templateRenderer
}

// MARK: - Instance Methods

private func makeTheme(value: TokenBoxShadowValue) -> BoxShadowToken.Theme {
BoxShadowToken.Theme(
color: value.color,
type: value.type,
x: value.x,
y: value.y,
blur: value.blur,
spread: value.spread
)
}

private func makeBoxShadowToken(
from dayTokenValue: TokenValue,
tokenValues: TokenValues
) throws -> BoxShadowToken? {
guard case let .boxShadow(dayValue) = dayTokenValue.type else {
return nil
}

guard let nightTokenValue = tokenValues.night.first(where: { $0.name == dayTokenValue.name }) else {
throw BoxShadowTokensGeneratorError(code: .nightValueNotFound(tokenName: dayTokenValue.name))
}

guard case let .boxShadow(nightValue) = nightTokenValue.type else {
throw BoxShadowTokensGeneratorError(code: .nightValueNotFound(tokenName: dayTokenValue.name))
}

return BoxShadowToken(
path: dayTokenValue.name.components(separatedBy: "."),
dayTheme: makeTheme(value: dayValue),
nightTheme: makeTheme(value: nightValue)
)
}

// MARK: -

func generate(renderParameters: RenderParameters, tokenValues: TokenValues) throws {
let boxShadows = try tokenValues.day
.compactMap { try makeBoxShadowToken(from: $0, tokenValues: tokenValues) }
.sorted { $0.path.joined() < $1.path.joined() }
let boxShadows = try boxShadowTokensContextProvider.fetchBoxShadowTokensContext(from: tokenValues)

try templateRenderer.renderTemplate(
renderParameters.template,
Expand Down
Loading