From 85cf24e655c0b0d7291aea018085cc9b261bcba1 Mon Sep 17 00:00:00 2001 From: nschpy Date: Wed, 26 Mar 2025 13:59:29 +0300 Subject: [PATCH 1/6] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB?= =?UTF-8?q?=20=D0=BE=D1=81=D0=BD=D0=BE=D0=B2=D0=BD=D1=8B=D0=B5=20=D1=82?= =?UTF-8?q?=D0=B8=D0=BF=D1=8B=20=D0=B4=D0=BB=D1=8F=20=D0=B3=D0=B5=D0=BD?= =?UTF-8?q?=D0=B5=D1=80=D0=B0=D1=86=D0=B8=D0=B8=20=D0=B3=D1=80=D0=B0=D0=B4?= =?UTF-8?q?=D0=B8=D0=B5=D0=BD=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/FigmaGen/Commands/TokensCommand.swift | 31 +++++++++++++++++++ Sources/FigmaGen/Dependencies.swift | 10 +++++- .../Tokens/Contexts/LinearGradientToken.swift | 21 +++++++++++++ .../Tokens/DefaultTokensGenerator.swift | 15 +++++++-- ...ltTokensGenerationParametersResolver.swift | 8 ++++- .../DefaultGradientTokensGenerator.swift | 29 +++++++++++++++++ .../Gradient/GradientTokensGenerator.swift | 3 ++ ...DefaultGradientTokensContextProvider.swift | 10 ++++++ .../GradientTokensContextProvider.swift | 6 ++++ .../Tokens/TokensTemplateConfiguration.swift | 3 ++ .../TokensGenerationParameters.swift | 1 + Templates/GradientTokens.stencil | 1 + 12 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 Sources/FigmaGen/Generators/Tokens/Contexts/LinearGradientToken.swift create mode 100644 Sources/FigmaGen/Generators/Tokens/Generators/Gradient/DefaultGradientTokensGenerator.swift create mode 100644 Sources/FigmaGen/Generators/Tokens/Generators/Gradient/GradientTokensGenerator.swift create mode 100644 Sources/FigmaGen/Generators/Tokens/Providers/Gradient/DefaultGradientTokensContextProvider.swift create mode 100644 Sources/FigmaGen/Generators/Tokens/Providers/Gradient/GradientTokensContextProvider.swift create mode 100644 Templates/GradientTokens.stencil diff --git a/Sources/FigmaGen/Commands/TokensCommand.swift b/Sources/FigmaGen/Commands/TokensCommand.swift index 28ad27c..aae5b32 100644 --- a/Sources/FigmaGen/Commands/TokensCommand.swift +++ b/Sources/FigmaGen/Commands/TokensCommand.swift @@ -260,6 +260,30 @@ final class TokensCommand: AsyncExecutableCommand { """ ) + let gradientTemplate = Key( + "--gradient-template", + description: """ + Path to the template file. + If no template is passed a default template will be used. + """ + ) + + let gradientTemplateOptions = VariadicKey( + "--gradient-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 gradientDestination = Key( + "--gradient-destination", + description: """ + The path to the file to generate. + By default, generated code will be printed on stdout. + """ + ) + // MARK: - Initializers init(generator: TokensGenerator) { @@ -345,6 +369,13 @@ extension TokensCommand { templateOptions: resolveTemplateOptions(bordersTemplateOptions.value), destination: bordersDestination.value ) + ], + gradient: [ + TemplateConfiguration( + template: gradientTemplate.value, + templateOptions: resolveTemplateOptions(gradientTemplateOptions.value), + destination: gradientDestination.value + ) ] ) ) diff --git a/Sources/FigmaGen/Dependencies.swift b/Sources/FigmaGen/Dependencies.swift index 25658fc..ff910bb 100644 --- a/Sources/FigmaGen/Dependencies.swift +++ b/Sources/FigmaGen/Dependencies.swift @@ -83,6 +83,8 @@ enum Dependencies { static let boxShadowTokensContextProvider: BoxShadowTokensContextProvider = DefaultBoxShadowTokensContextProvider() + static let gradientTokensContextProvider: GradientTokensContextProvider = DefaultGradientTokensContextProvider() + // MARK: - static let templateContextCoder: TemplateContextCoder = DefaultTemplateContextCoder() @@ -184,6 +186,11 @@ enum Dependencies { templateRenderer: templateRenderer ) + static let gradientTokensGenerator: GradientTokensGenerator = DefaultGradientTokensGenerator( + templateRenderer: templateRenderer, + provider: gradientTokensContextProvider + ) + static let tokensGenerator: TokensGenerator = DefaultTokensGenerator( tokensProvider: tokensProvider, tokensGenerationParametersResolver: tokensGenerationParametersResolver, @@ -194,7 +201,8 @@ enum Dependencies { boxShadowTokensGenerator: boxShadowTokensGenerator, themeTokensGenerator: themeTokensGenerator, spacingTokensGenerator: spacingTokensGenerator, - bordersTokensGenerator: borderTokensGenerator + bordersTokensGenerator: borderTokensGenerator, + gradientTokensGenerator: gradientTokensGenerator ) static let libraryGenerator: LibraryGenerator = DefaultLibraryGenerator( diff --git a/Sources/FigmaGen/Generators/Tokens/Contexts/LinearGradientToken.swift b/Sources/FigmaGen/Generators/Tokens/Contexts/LinearGradientToken.swift new file mode 100644 index 0000000..df7ff8f --- /dev/null +++ b/Sources/FigmaGen/Generators/Tokens/Contexts/LinearGradientToken.swift @@ -0,0 +1,21 @@ +import Foundation + +struct LinearGradientToken: Encodable { + + struct Stop: Encodable { + let color: ColorToken + let location: String + } + + struct ThemeValue: Encodable { + let stops: [Stop] + let angle: String + } + + let path: [String] + let name: String + + let dayTheme: ThemeValue + let nightTheme: ThemeValue + let zpDayTheme: ThemeValue +} diff --git a/Sources/FigmaGen/Generators/Tokens/DefaultTokensGenerator.swift b/Sources/FigmaGen/Generators/Tokens/DefaultTokensGenerator.swift index 5cce8aa..135cb61 100644 --- a/Sources/FigmaGen/Generators/Tokens/DefaultTokensGenerator.swift +++ b/Sources/FigmaGen/Generators/Tokens/DefaultTokensGenerator.swift @@ -16,7 +16,7 @@ final class DefaultTokensGenerator: TokensGenerator { let themeTokensGenerator: ThemeTokensGenerator let spacingTokensGenerator: SpacingTokensGenerator let bordersTokensGenerator: BorderTokensGenerator - + let gradientTokensGenerator: GradientTokensGenerator // MARK: - Initializers @@ -30,7 +30,8 @@ final class DefaultTokensGenerator: TokensGenerator { boxShadowTokensGenerator: BoxShadowTokensGenerator, themeTokensGenerator: ThemeTokensGenerator, spacingTokensGenerator: SpacingTokensGenerator, - bordersTokensGenerator: BorderTokensGenerator + bordersTokensGenerator: BorderTokensGenerator, + gradientTokensGenerator: GradientTokensGenerator ) { self.tokensProvider = tokensProvider self.tokensGenerationParametersResolver = tokensGenerationParametersResolver @@ -42,6 +43,7 @@ final class DefaultTokensGenerator: TokensGenerator { self.themeTokensGenerator = themeTokensGenerator self.spacingTokensGenerator = spacingTokensGenerator self.bordersTokensGenerator = bordersTokensGenerator + self.gradientTokensGenerator = gradientTokensGenerator } // MARK: - Instance Methods @@ -57,6 +59,7 @@ final class DefaultTokensGenerator: TokensGenerator { try generateThemeTokens(parameters: parameters, tokenValues: tokenValues) try generateSpacingTokens(parameters: parameters, tokenValues: tokenValues) try generateBorderTokens(parameters: parameters, tokenValues: tokenValues) + try generateGradientTokens(parameters: parameters, tokenValues: tokenValues) } private func fetchTokens(from parameters: TokensGenerationParameters) async throws -> TokenValues { @@ -133,6 +136,14 @@ final class DefaultTokensGenerator: TokensGenerator { ) } + private func generateGradientTokens(parameters: TokensGenerationParameters, tokenValues: TokenValues) throws { + try generateTokens( + gradientTokensGenerator, + renderParameters: parameters.tokens.gradientRenderParameters, + tokenValues: tokenValues + ) + } + private func generateTokens( _ generator: BaseTokenGenerator, renderParameters: [RenderParameters]?, diff --git a/Sources/FigmaGen/Generators/Tokens/GenerationParametersResolver/DefaultTokensGenerationParametersResolver.swift b/Sources/FigmaGen/Generators/Tokens/GenerationParametersResolver/DefaultTokensGenerationParametersResolver.swift index 8add0c3..0c68907 100644 --- a/Sources/FigmaGen/Generators/Tokens/GenerationParametersResolver/DefaultTokensGenerationParametersResolver.swift +++ b/Sources/FigmaGen/Generators/Tokens/GenerationParametersResolver/DefaultTokensGenerationParametersResolver.swift @@ -94,6 +94,11 @@ final class DefaultTokensGenerationParametersResolver: TokensGenerationParameter defaultTemplateType: .native(name: "BorderTokens") ) + let gradientRenderParameters = renderParametersResolver.resolveRenderParameters( + templates: configuration.templates?.gradient, + defaultTemplateType: .native(name: "GradientTokens") + ) + return TokensGenerationParameters( file: file, remoteFile: remoteFile, @@ -105,7 +110,8 @@ final class DefaultTokensGenerationParametersResolver: TokensGenerationParameter boxShadowRenderParameters: boxShadowRenderParameters, themeRenderParameters: themeRenderParameters, spacingRenderParameters: spacingRenderParameters, - bordersRenderParameters: borderRenderParameters + bordersRenderParameters: borderRenderParameters, + gradientRenderParameters: gradientRenderParameters ) ) } diff --git a/Sources/FigmaGen/Generators/Tokens/Generators/Gradient/DefaultGradientTokensGenerator.swift b/Sources/FigmaGen/Generators/Tokens/Generators/Gradient/DefaultGradientTokensGenerator.swift new file mode 100644 index 0000000..e2ce972 --- /dev/null +++ b/Sources/FigmaGen/Generators/Tokens/Generators/Gradient/DefaultGradientTokensGenerator.swift @@ -0,0 +1,29 @@ +import Foundation + +struct DefaultGradientTokensGenerator: GradientTokensGenerator { + + // MARK: - Instance properties + + private let templateRenderer: TemplateRenderer + private let provider: GradientTokensContextProvider + + init( + templateRenderer: TemplateRenderer, + provider: GradientTokensContextProvider + ) { + self.templateRenderer = templateRenderer + self.provider = provider + } + + // MARK: - GradientTokensGenerator + + func generate(renderParameters: RenderParameters, tokenValues: TokenValues) throws { + let gradientContext = try provider.extractContext(from: tokenValues) + + try templateRenderer.renderTemplate( + renderParameters.template, + to: renderParameters.destination, + context: ["gradient": gradientContext] + ) + } +} diff --git a/Sources/FigmaGen/Generators/Tokens/Generators/Gradient/GradientTokensGenerator.swift b/Sources/FigmaGen/Generators/Tokens/Generators/Gradient/GradientTokensGenerator.swift new file mode 100644 index 0000000..0cb5b79 --- /dev/null +++ b/Sources/FigmaGen/Generators/Tokens/Generators/Gradient/GradientTokensGenerator.swift @@ -0,0 +1,3 @@ +import Foundation + +protocol GradientTokensGenerator: BaseTokenGenerator { } diff --git a/Sources/FigmaGen/Generators/Tokens/Providers/Gradient/DefaultGradientTokensContextProvider.swift b/Sources/FigmaGen/Generators/Tokens/Providers/Gradient/DefaultGradientTokensContextProvider.swift new file mode 100644 index 0000000..62e51d2 --- /dev/null +++ b/Sources/FigmaGen/Generators/Tokens/Providers/Gradient/DefaultGradientTokensContextProvider.swift @@ -0,0 +1,10 @@ +import Foundation + +struct DefaultGradientTokensContextProvider: GradientTokensContextProvider { + + // MARK: - GradientTokensContextProvider + + func extractContext(from tokenValues: TokenValues) throws -> [String : Any] { + [:] + } +} diff --git a/Sources/FigmaGen/Generators/Tokens/Providers/Gradient/GradientTokensContextProvider.swift b/Sources/FigmaGen/Generators/Tokens/Providers/Gradient/GradientTokensContextProvider.swift new file mode 100644 index 0000000..940f3b7 --- /dev/null +++ b/Sources/FigmaGen/Generators/Tokens/Providers/Gradient/GradientTokensContextProvider.swift @@ -0,0 +1,6 @@ +import Foundation + +protocol GradientTokensContextProvider { + + func extractContext(from tokenValues: TokenValues) throws -> [String : Any] +} diff --git a/Sources/FigmaGen/Models/Configuration/Tokens/TokensTemplateConfiguration.swift b/Sources/FigmaGen/Models/Configuration/Tokens/TokensTemplateConfiguration.swift index bf165db..9ceeb54 100644 --- a/Sources/FigmaGen/Models/Configuration/Tokens/TokensTemplateConfiguration.swift +++ b/Sources/FigmaGen/Models/Configuration/Tokens/TokensTemplateConfiguration.swift @@ -13,6 +13,7 @@ struct TokensTemplateConfiguration { let theme: [TemplateConfiguration]? let spacing: [TemplateConfiguration]? let borders: [TemplateConfiguration]? + let gradient: [TemplateConfiguration]? } // MARK: - Decodable @@ -30,6 +31,7 @@ extension TokensTemplateConfiguration: Decodable { case theme case spacing case borders + case gradient } // MARK: - Initializers @@ -51,5 +53,6 @@ extension TokensTemplateConfiguration: Decodable { theme = try container.decodeIfPresent(TemplateConfigurationWrapper.self, forKey: .theme)?.templates spacing = try container.decodeIfPresent(TemplateConfigurationWrapper.self, forKey: .spacing)?.templates borders = try? container.decodeIfPresent(TemplateConfigurationWrapper.self, forKey: .borders)?.templates + gradient = try? container.decodeIfPresent(TemplateConfigurationWrapper.self, forKey: .borders)?.templates } } diff --git a/Sources/FigmaGen/Models/Parameters/TokensGenerationParameters.swift b/Sources/FigmaGen/Models/Parameters/TokensGenerationParameters.swift index 6c4a7b0..b500391 100644 --- a/Sources/FigmaGen/Models/Parameters/TokensGenerationParameters.swift +++ b/Sources/FigmaGen/Models/Parameters/TokensGenerationParameters.swift @@ -16,6 +16,7 @@ struct TokensGenerationParameters { let themeRenderParameters: [RenderParameters]? let spacingRenderParameters: [RenderParameters]? let bordersRenderParameters: [RenderParameters]? + let gradientRenderParameters: [RenderParameters]? } // MARK: - Instance Properties diff --git a/Templates/GradientTokens.stencil b/Templates/GradientTokens.stencil new file mode 100644 index 0000000..17e803c --- /dev/null +++ b/Templates/GradientTokens.stencil @@ -0,0 +1 @@ +# TODO: mi.fedorov доделать шаблон From 8ff0b435512871c6060c71a6a104f77ef9ae088d Mon Sep 17 00:00:00 2001 From: nschpy Date: Thu, 27 Mar 2025 01:51:55 +0300 Subject: [PATCH 2/6] =?UTF-8?q?=D0=9E=D1=81=D0=BD=D0=BE=D0=B2=D0=BD=D0=B0?= =?UTF-8?q?=D1=8F=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA=D0=B0=20=D1=81=D0=BE?= =?UTF-8?q?=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8=D1=8F=20=D1=82=D0=BE=D0=BA=D0=B5?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2=20=D0=B3=D1=80=D0=B0=D0=B4=D0=B8=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/FigmaGen/Dependencies.swift | 4 +- .../Tokens/Contexts/LinearGradientToken.swift | 19 +-- ...DefaultGradientTokensContextProvider.swift | 153 +++++++++++++++++- Sources/FigmaGen/Models/LinearGradient.swift | 15 ++ 4 files changed, 180 insertions(+), 11 deletions(-) diff --git a/Sources/FigmaGen/Dependencies.swift b/Sources/FigmaGen/Dependencies.swift index ff910bb..a307768 100644 --- a/Sources/FigmaGen/Dependencies.swift +++ b/Sources/FigmaGen/Dependencies.swift @@ -83,7 +83,9 @@ enum Dependencies { static let boxShadowTokensContextProvider: BoxShadowTokensContextProvider = DefaultBoxShadowTokensContextProvider() - static let gradientTokensContextProvider: GradientTokensContextProvider = DefaultGradientTokensContextProvider() + static let gradientTokensContextProvider: GradientTokensContextProvider = DefaultGradientTokensContextProvider( + tokensResolver: tokensResolver + ) // MARK: - diff --git a/Sources/FigmaGen/Generators/Tokens/Contexts/LinearGradientToken.swift b/Sources/FigmaGen/Generators/Tokens/Contexts/LinearGradientToken.swift index df7ff8f..53c4dfd 100644 --- a/Sources/FigmaGen/Generators/Tokens/Contexts/LinearGradientToken.swift +++ b/Sources/FigmaGen/Generators/Tokens/Contexts/LinearGradientToken.swift @@ -2,20 +2,21 @@ import Foundation struct LinearGradientToken: Encodable { - struct Stop: Encodable { - let color: ColorToken - let location: String + struct ColorStop: Encodable { + let color: String + let percentage: CGFloat } - struct ThemeValue: Encodable { - let stops: [Stop] - let angle: String + struct GradientThemeValue: Encodable { + let stops: [ColorStop] + let startPoint: CGPoint + let endPoint: CGPoint } let path: [String] let name: String - let dayTheme: ThemeValue - let nightTheme: ThemeValue - let zpDayTheme: ThemeValue + let dayTheme: GradientThemeValue + let nightTheme: GradientThemeValue + let zpDayTheme: GradientThemeValue } diff --git a/Sources/FigmaGen/Generators/Tokens/Providers/Gradient/DefaultGradientTokensContextProvider.swift b/Sources/FigmaGen/Generators/Tokens/Providers/Gradient/DefaultGradientTokensContextProvider.swift index 62e51d2..9692c26 100644 --- a/Sources/FigmaGen/Generators/Tokens/Providers/Gradient/DefaultGradientTokensContextProvider.swift +++ b/Sources/FigmaGen/Generators/Tokens/Providers/Gradient/DefaultGradientTokensContextProvider.swift @@ -2,9 +2,160 @@ import Foundation struct DefaultGradientTokensContextProvider: GradientTokensContextProvider { + let tokensResolver: TokensResolver + + init(tokensResolver: TokensResolver) { + self.tokensResolver = tokensResolver + } + + // MARK: - Private methods + + private func resolvePoints(angle: CGFloat) -> (start: CGPoint, end: CGPoint)? { + let start = 3.0 * .pi / 2 + let u = start + angle + + let ucos = cos(u) + let usin = sin(u) + + let xedge = ucos > 0 ? 1.0 : 0 + let yedge = usin > 0 ? 1.0 : 0 + + let tx = ucos == 0 ? nil : (xedge - 0.5) / ucos + let ty = usin == 0 ? nil : (yedge - 0.5) / usin + + let t = [tx, ty] + .compactMap(\.self) + .filter { $0 > 0} + .min() + + guard let t else { + // Невозможно вычислить пересечение с границей + return nil + } + + let endPoint = CGPoint(x: 0.5 + t * ucos, y: 0.5 + t * usin) + let startPoint = CGPoint(x: 0.5 - t * ucos, y: 0.5 - t * usin) + + return (start: startPoint, end: endPoint) + } + + private func resolveThemeValue( + from gradient: LinearGradient, + tokenName: String + ) -> LinearGradientToken.GradientThemeValue? { + guard let points = resolvePoints(angle: gradient.radians) else { + return nil + } + + return LinearGradientToken.GradientThemeValue( + stops: gradient.colorStopList.compactMap { stop in + guard let percentage = Double(stop.percentage) else { + return nil + } + + return .init( + color: stop.color.hexString, + percentage: percentage + ) + }, + startPoint: points.start, + endPoint: points.end + ) + } + + private func resolveGradientToken( + tokenName: String, + fallbackColorToken: LinearGradientToken.GradientThemeValue, + tokenValues: TokenValues, + theme: Theme + ) throws -> LinearGradientToken.GradientThemeValue { + let themeData: (tokenValues: [TokenValue], warningPrefix: String) + switch theme { + case .night: + themeData = (tokenValues.hhNight, "Night") + + case .zpDay: + themeData = (tokenValues.zpDay, "ZpDay") + + case .day, .undefined: + themeData = ([], "") + } + + guard let themeToken = themeData.tokenValues.first(where: { $0.name == tokenName }) else { + return fallbackColorToken + } + + guard case .color(let themeValue) = themeToken.type else { + return fallbackColorToken + } + + let gradient = try tokensResolver.resolveLinearGradientValue( + themeValue, + tokenValues: tokenValues, + theme: theme + ) + + return resolveThemeValue(from: gradient, tokenName: tokenName) ?? fallbackColorToken + } + + private func createGradientToken( + _ gradientValue: String, + tokenName: String, + tokenValues: TokenValues + ) throws -> LinearGradientToken? { + let path = tokenName.components(separatedBy: ".") + let dayGradient = try tokensResolver.resolveLinearGradientValue( + gradientValue, + tokenValues: tokenValues, + theme: .day + ) + + guard let dayToken = resolveThemeValue(from: dayGradient, tokenName: tokenName) else { + return nil + } + + let nightToken = try resolveGradientToken( + tokenName: tokenName, + fallbackColorToken: dayToken, + tokenValues: tokenValues, + theme: .night + ) + + let zpDayToken = try resolveGradientToken( + tokenName: tokenName, + fallbackColorToken: dayToken, + tokenValues: tokenValues, + theme: .zpDay + ) + + return LinearGradientToken( + path: path, + name: tokenName, + dayTheme: dayToken, + nightTheme: nightToken, + zpDayTheme: zpDayToken + ) + } + + // TODO: @mi.fedorov поддержать мульти-темы + private func extractGradientToken( + from token: TokenValue, + tokenValues: TokenValues + ) throws -> LinearGradientToken? { + guard case .color(let dayValue) = token.type, dayValue.contains("gradient") else { + return nil + } + + return try createGradientToken(dayValue, tokenName: token.name, tokenValues: tokenValues) + } + // MARK: - GradientTokensContextProvider func extractContext(from tokenValues: TokenValues) throws -> [String : Any] { - [:] + let gradient = try tokenValues.hhDay.compactMap { + try extractGradientToken(from: $0, tokenValues: tokenValues) + } + + return [:] // TODO: Make structure } } diff --git a/Sources/FigmaGen/Models/LinearGradient.swift b/Sources/FigmaGen/Models/LinearGradient.swift index 92a0120..c97b708 100644 --- a/Sources/FigmaGen/Models/LinearGradient.swift +++ b/Sources/FigmaGen/Models/LinearGradient.swift @@ -17,3 +17,18 @@ struct LinearGradient: Codable, Hashable { let angle: String let colorStopList: [LinearColorStop] } + +extension LinearGradient { + + var radians: Double { + if angle.hasSuffix("deg"), let deg = Double(angle.replacingOccurrences(of: "deg", with: "")) { + return deg * .pi / 180 + } + + if angle.hasSuffix("rad"), let rad = Double(angle.replacingOccurrences(of: "rad", with: "")) { + return rad + } + + return Double(angle) ?? .zero + } +} From 90acb2bc3dfeef72c9d029931647db2f3ca73dab Mon Sep 17 00:00:00 2001 From: nschpy Date: Fri, 28 Mar 2025 01:48:28 +0300 Subject: [PATCH 3/6] =?UTF-8?q?=D0=93=D0=B5=D0=BD=D0=B5=D1=80=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=B3=D1=80=D0=B0=D0=B4=D0=B8=D0=B5=D0=BD=D1=82?= =?UTF-8?q?=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/FigmaGen/Dependencies.swift | 5 +- .../Tokens/Contexts/ColorToken.swift | 2 +- .../Tokens/Contexts/LinearGradientToken.swift | 2 +- .../Tokens/Contexts/TokenProtocol.swift | 12 ++++ .../Color/DefaultColorTokensGenerator.swift | 2 +- .../DefaultGradientTokensGenerator.swift | 10 +-- .../Theme/DefaultThemeTokensGenerator.swift | 9 ++- ...olorTokensContextProvider+Extensions.swift | 42 ++++++++++++ .../ColorTokensContextProvider.swift | 2 +- .../DefaultColorTokensContextProvider.swift | 38 +---------- ...DefaultGradientTokensContextProvider.swift | 26 +++++-- .../GradientTokensContextProvider.swift | 6 -- .../Resolver/DefaultTokensResolver.swift | 67 ++++++++++++++++--- .../Tokens/Resolver/TokensResolver.swift | 1 + .../Tokens/TokensTemplateConfiguration.swift | 2 +- Templates/GradientTokens.stencil | 48 ++++++++++++- 16 files changed, 202 insertions(+), 72 deletions(-) create mode 100644 Sources/FigmaGen/Generators/Tokens/Contexts/TokenProtocol.swift create mode 100644 Sources/FigmaGen/Generators/Tokens/Providers/ColorTokensContext/ColorTokensContextProvider+Extensions.swift rename Sources/FigmaGen/Generators/Tokens/Providers/{Gradient => ColorTokensContext}/DefaultGradientTokensContextProvider.swift (84%) delete mode 100644 Sources/FigmaGen/Generators/Tokens/Providers/Gradient/GradientTokensContextProvider.swift diff --git a/Sources/FigmaGen/Dependencies.swift b/Sources/FigmaGen/Dependencies.swift index a307768..8201736 100644 --- a/Sources/FigmaGen/Dependencies.swift +++ b/Sources/FigmaGen/Dependencies.swift @@ -83,7 +83,7 @@ enum Dependencies { static let boxShadowTokensContextProvider: BoxShadowTokensContextProvider = DefaultBoxShadowTokensContextProvider() - static let gradientTokensContextProvider: GradientTokensContextProvider = DefaultGradientTokensContextProvider( + static let gradientTokensContextProvider: ColorTokensContextProvider = DefaultGradientTokensContextProvider( tokensResolver: tokensResolver ) @@ -174,6 +174,7 @@ enum Dependencies { static let themeTokensGenerator: ThemeTokensGenerator = DefaultThemeTokensGenerator( colorTokensContextProvider: colorTokensContextProvider, + gradientTokensContextProvider: gradientTokensContextProvider, boxShadowsContextProvider: boxShadowTokensContextProvider, templateRenderer: templateRenderer ) @@ -190,7 +191,7 @@ enum Dependencies { static let gradientTokensGenerator: GradientTokensGenerator = DefaultGradientTokensGenerator( templateRenderer: templateRenderer, - provider: gradientTokensContextProvider + gradientProvider: gradientTokensContextProvider ) static let tokensGenerator: TokensGenerator = DefaultTokensGenerator( diff --git a/Sources/FigmaGen/Generators/Tokens/Contexts/ColorToken.swift b/Sources/FigmaGen/Generators/Tokens/Contexts/ColorToken.swift index 2a6e788..639ff65 100644 --- a/Sources/FigmaGen/Generators/Tokens/Contexts/ColorToken.swift +++ b/Sources/FigmaGen/Generators/Tokens/Contexts/ColorToken.swift @@ -1,6 +1,6 @@ import Foundation -struct ColorToken: Encodable { +struct ColorToken: TokenProtocol, Encodable { // MARK: - Nested Types diff --git a/Sources/FigmaGen/Generators/Tokens/Contexts/LinearGradientToken.swift b/Sources/FigmaGen/Generators/Tokens/Contexts/LinearGradientToken.swift index 53c4dfd..c74b046 100644 --- a/Sources/FigmaGen/Generators/Tokens/Contexts/LinearGradientToken.swift +++ b/Sources/FigmaGen/Generators/Tokens/Contexts/LinearGradientToken.swift @@ -1,6 +1,6 @@ import Foundation -struct LinearGradientToken: Encodable { +struct LinearGradientToken: TokenProtocol, Encodable { struct ColorStop: Encodable { let color: String diff --git a/Sources/FigmaGen/Generators/Tokens/Contexts/TokenProtocol.swift b/Sources/FigmaGen/Generators/Tokens/Contexts/TokenProtocol.swift new file mode 100644 index 0000000..d9198f3 --- /dev/null +++ b/Sources/FigmaGen/Generators/Tokens/Contexts/TokenProtocol.swift @@ -0,0 +1,12 @@ +import Foundation + +protocol TokenProtocol { + var name: String { get } + var path: [String] { get } +} + +extension TokenProtocol { + var name: String { + path.joined(separator: ".") + } +} diff --git a/Sources/FigmaGen/Generators/Tokens/Generators/Color/DefaultColorTokensGenerator.swift b/Sources/FigmaGen/Generators/Tokens/Generators/Color/DefaultColorTokensGenerator.swift index 88014fb..8e93fd6 100644 --- a/Sources/FigmaGen/Generators/Tokens/Generators/Color/DefaultColorTokensGenerator.swift +++ b/Sources/FigmaGen/Generators/Tokens/Generators/Color/DefaultColorTokensGenerator.swift @@ -20,7 +20,7 @@ final class DefaultColorTokensGenerator: ColorTokensGenerator { // MARK: - Instance Methods func generate(renderParameters: RenderParameters, tokenValues: TokenValues) throws { - let context = try colorTokensContextProvider.fetchColorTokensContext(from: tokenValues) + let context = try colorTokensContextProvider.extractTokenContext(from: tokenValues) try templateRenderer.renderTemplate( renderParameters.template, diff --git a/Sources/FigmaGen/Generators/Tokens/Generators/Gradient/DefaultGradientTokensGenerator.swift b/Sources/FigmaGen/Generators/Tokens/Generators/Gradient/DefaultGradientTokensGenerator.swift index e2ce972..c347a5b 100644 --- a/Sources/FigmaGen/Generators/Tokens/Generators/Gradient/DefaultGradientTokensGenerator.swift +++ b/Sources/FigmaGen/Generators/Tokens/Generators/Gradient/DefaultGradientTokensGenerator.swift @@ -5,25 +5,25 @@ struct DefaultGradientTokensGenerator: GradientTokensGenerator { // MARK: - Instance properties private let templateRenderer: TemplateRenderer - private let provider: GradientTokensContextProvider + private let provider: ColorTokensContextProvider init( templateRenderer: TemplateRenderer, - provider: GradientTokensContextProvider + gradientProvider: ColorTokensContextProvider ) { self.templateRenderer = templateRenderer - self.provider = provider + self.provider = gradientProvider } // MARK: - GradientTokensGenerator func generate(renderParameters: RenderParameters, tokenValues: TokenValues) throws { - let gradientContext = try provider.extractContext(from: tokenValues) + let gradientContext = try provider.extractTokenContext(from: tokenValues) try templateRenderer.renderTemplate( renderParameters.template, to: renderParameters.destination, - context: ["gradient": gradientContext] + context: ["gradients": gradientContext] ) } } diff --git a/Sources/FigmaGen/Generators/Tokens/Generators/Theme/DefaultThemeTokensGenerator.swift b/Sources/FigmaGen/Generators/Tokens/Generators/Theme/DefaultThemeTokensGenerator.swift index 5795c5f..782f478 100644 --- a/Sources/FigmaGen/Generators/Tokens/Generators/Theme/DefaultThemeTokensGenerator.swift +++ b/Sources/FigmaGen/Generators/Tokens/Generators/Theme/DefaultThemeTokensGenerator.swift @@ -5,6 +5,7 @@ final class DefaultThemeTokensGenerator: ThemeTokensGenerator { // MARK: - Instance Properties let colorTokensContextProvider: ColorTokensContextProvider + let gradientTokensContextProvider: ColorTokensContextProvider let boxShadowsContextProvider: BoxShadowTokensContextProvider let templateRenderer: TemplateRenderer @@ -12,10 +13,12 @@ final class DefaultThemeTokensGenerator: ThemeTokensGenerator { init( colorTokensContextProvider: ColorTokensContextProvider, + gradientTokensContextProvider: ColorTokensContextProvider, boxShadowsContextProvider: BoxShadowTokensContextProvider, templateRenderer: TemplateRenderer ) { self.colorTokensContextProvider = colorTokensContextProvider + self.gradientTokensContextProvider = gradientTokensContextProvider self.boxShadowsContextProvider = boxShadowsContextProvider self.templateRenderer = templateRenderer } @@ -23,15 +26,17 @@ final class DefaultThemeTokensGenerator: ThemeTokensGenerator { // MARK: - Instance Methods func generate(renderParameters: RenderParameters, tokenValues: TokenValues) throws { - let colorsContext = try colorTokensContextProvider.fetchColorTokensContext(from: tokenValues) + let colorsContext = try colorTokensContextProvider.extractTokenContext(from: tokenValues) let boxShadowsContext = try boxShadowsContextProvider.fetchBoxShadowTokensContext(from: tokenValues) + let gradientsContext = try gradientTokensContextProvider.extractTokenContext(from: tokenValues) try templateRenderer.renderTemplate( renderParameters.template, to: renderParameters.destination, context: [ "colors": colorsContext, - "boxShadows": boxShadowsContext + "boxShadows": boxShadowsContext, + "gradients": gradientsContext ] ) } diff --git a/Sources/FigmaGen/Generators/Tokens/Providers/ColorTokensContext/ColorTokensContextProvider+Extensions.swift b/Sources/FigmaGen/Generators/Tokens/Providers/ColorTokensContext/ColorTokensContextProvider+Extensions.swift new file mode 100644 index 0000000..55de38d --- /dev/null +++ b/Sources/FigmaGen/Generators/Tokens/Providers/ColorTokensContext/ColorTokensContextProvider+Extensions.swift @@ -0,0 +1,42 @@ +import Foundation + +extension ColorTokensContextProvider { + + func structure( + tokens: [T], + atNamePath namePath: [String] = [], + contextName: String = "tokens" + ) -> [String: Any] { + var structuredTokens: [String: Any] = [:] + + if let name = namePath.last { + structuredTokens["name"] = name + } + + if !namePath.isEmpty { + structuredTokens["path"] = namePath + } + + let filteredTokens = tokens + .filter { $0.path.count == namePath.count + 1 } + .sorted { $0.name.lowercased() < $1.name.lowercased() } + + if !filteredTokens.isEmpty { + structuredTokens[contextName] = filteredTokens + } + + let childTokens = tokens.filter { $0.path.count > namePath.count + 1 } + + let children = Dictionary(grouping: childTokens) { $0.path[namePath.count] } + .sorted { $0.key < $1.key } + .map { name, tokens in + structure(tokens: tokens, atNamePath: namePath + [name], contextName: contextName) + } + + if !children.isEmpty { + structuredTokens["children"] = children + } + + return structuredTokens + } +} diff --git a/Sources/FigmaGen/Generators/Tokens/Providers/ColorTokensContext/ColorTokensContextProvider.swift b/Sources/FigmaGen/Generators/Tokens/Providers/ColorTokensContext/ColorTokensContextProvider.swift index cbdfbce..785b0b8 100644 --- a/Sources/FigmaGen/Generators/Tokens/Providers/ColorTokensContext/ColorTokensContextProvider.swift +++ b/Sources/FigmaGen/Generators/Tokens/Providers/ColorTokensContext/ColorTokensContextProvider.swift @@ -4,5 +4,5 @@ protocol ColorTokensContextProvider { // MARK: - Instance Methods - func fetchColorTokensContext(from tokenValues: TokenValues) throws -> [String: Any] + func extractTokenContext(from tokenValues: TokenValues) throws -> [String: Any] } diff --git a/Sources/FigmaGen/Generators/Tokens/Providers/ColorTokensContext/DefaultColorTokensContextProvider.swift b/Sources/FigmaGen/Generators/Tokens/Providers/ColorTokensContext/DefaultColorTokensContextProvider.swift index 8973278..10c25b9 100644 --- a/Sources/FigmaGen/Generators/Tokens/Providers/ColorTokensContext/DefaultColorTokensContextProvider.swift +++ b/Sources/FigmaGen/Generators/Tokens/Providers/ColorTokensContext/DefaultColorTokensContextProvider.swift @@ -101,43 +101,9 @@ final class DefaultColorTokensContextProvider: ColorTokensContextProvider { ) } - private func structure(tokenColors: [ColorToken], atNamePath namePath: [String] = []) -> [String: Any] { - var structuredColors: [String: Any] = [:] - - if let name = namePath.last { - structuredColors["name"] = name - } - - if !namePath.isEmpty { - structuredColors["path"] = namePath - } - - let colors = tokenColors - .filter { $0.path.count == namePath.count + 1 } - .sorted { $0.name.lowercased() < $1.name.lowercased() } - - if !colors.isEmpty { - structuredColors["colors"] = colors - } - - let childTokenColors = tokenColors.filter { $0.path.count > namePath.count + 1 } - - let children = Dictionary(grouping: childTokenColors) { $0.path[namePath.count] } - .sorted { $0.key < $1.key } - .map { name, colors in - structure(tokenColors: colors, atNamePath: namePath + [name]) - } - - if !children.isEmpty { - structuredColors["children"] = children - } - - return structuredColors - } - // MARK: - - func fetchColorTokensContext(from tokenValues: TokenValues) throws -> [String: Any] { + func extractTokenContext(from tokenValues: TokenValues) throws -> [String: Any] { let colors: [ColorToken] = try tokenValues.hhDay.compactMap { (token: TokenValue) in guard case .color(let dayValue) = token.type else { return nil @@ -157,6 +123,6 @@ final class DefaultColorTokensContextProvider: ColorTokensContextProvider { ) } - return structure(tokenColors: colors) + return structure(tokens: colors, contextName: "colors") } } diff --git a/Sources/FigmaGen/Generators/Tokens/Providers/Gradient/DefaultGradientTokensContextProvider.swift b/Sources/FigmaGen/Generators/Tokens/Providers/ColorTokensContext/DefaultGradientTokensContextProvider.swift similarity index 84% rename from Sources/FigmaGen/Generators/Tokens/Providers/Gradient/DefaultGradientTokensContextProvider.swift rename to Sources/FigmaGen/Generators/Tokens/Providers/ColorTokensContext/DefaultGradientTokensContextProvider.swift index 9692c26..62db6e8 100644 --- a/Sources/FigmaGen/Generators/Tokens/Providers/Gradient/DefaultGradientTokensContextProvider.swift +++ b/Sources/FigmaGen/Generators/Tokens/Providers/ColorTokensContext/DefaultGradientTokensContextProvider.swift @@ -1,6 +1,6 @@ import Foundation -struct DefaultGradientTokensContextProvider: GradientTokensContextProvider { +struct DefaultGradientTokensContextProvider: ColorTokensContextProvider { let tokensResolver: TokensResolver @@ -49,7 +49,8 @@ struct DefaultGradientTokensContextProvider: GradientTokensContextProvider { return LinearGradientToken.GradientThemeValue( stops: gradient.colorStopList.compactMap { stop in - guard let percentage = Double(stop.percentage) else { + let percentage = stop.percentage.replacingOccurrences(of: "%", with: "") + guard let percentage = Double(percentage) else { return nil } @@ -101,9 +102,9 @@ struct DefaultGradientTokensContextProvider: GradientTokensContextProvider { private func createGradientToken( _ gradientValue: String, tokenName: String, + path: [String], tokenValues: TokenValues ) throws -> LinearGradientToken? { - let path = tokenName.components(separatedBy: ".") let dayGradient = try tokensResolver.resolveLinearGradientValue( gradientValue, tokenValues: tokenValues, @@ -142,20 +143,31 @@ struct DefaultGradientTokensContextProvider: GradientTokensContextProvider { from token: TokenValue, tokenValues: TokenValues ) throws -> LinearGradientToken? { - guard case .color(let dayValue) = token.type, dayValue.contains("gradient") else { + let path = token.name.components(separatedBy: ".") + + guard + case .color(let dayValue) = token.type, + dayValue.contains("gradient"), + path[0] != "color" + else { return nil } - return try createGradientToken(dayValue, tokenName: token.name, tokenValues: tokenValues) + return try createGradientToken( + dayValue, + tokenName: token.name, + path: path, + tokenValues: tokenValues + ) } // MARK: - GradientTokensContextProvider - func extractContext(from tokenValues: TokenValues) throws -> [String : Any] { + func extractTokenContext(from tokenValues: TokenValues) throws -> [String : Any] { let gradient = try tokenValues.hhDay.compactMap { try extractGradientToken(from: $0, tokenValues: tokenValues) } - return [:] // TODO: Make structure + return structure(tokens: gradient, contextName: "gradient") } } diff --git a/Sources/FigmaGen/Generators/Tokens/Providers/Gradient/GradientTokensContextProvider.swift b/Sources/FigmaGen/Generators/Tokens/Providers/Gradient/GradientTokensContextProvider.swift deleted file mode 100644 index 940f3b7..0000000 --- a/Sources/FigmaGen/Generators/Tokens/Providers/Gradient/GradientTokensContextProvider.swift +++ /dev/null @@ -1,6 +0,0 @@ -import Foundation - -protocol GradientTokensContextProvider { - - func extractContext(from tokenValues: TokenValues) throws -> [String : Any] -} diff --git a/Sources/FigmaGen/Generators/Tokens/Resolver/DefaultTokensResolver.swift b/Sources/FigmaGen/Generators/Tokens/Resolver/DefaultTokensResolver.swift index c4bd3f2..3d0266c 100644 --- a/Sources/FigmaGen/Generators/Tokens/Resolver/DefaultTokensResolver.swift +++ b/Sources/FigmaGen/Generators/Tokens/Resolver/DefaultTokensResolver.swift @@ -68,6 +68,45 @@ final class DefaultTokensResolver: TokensResolver { return try makeColor(hex: value, alpha: 1.0) } + private func resolveRGBAWithHex( + hex: String, + alphaPercent: String, + value: String + ) throws -> Color { + let alpha = alphaPercent.replacingOccurrences(of: "%", with: "") + guard let alpha = Double(alpha) else { + throw TokensGeneratorError(code: .invalidAlphaComponent(alpha: alphaPercent + value)) + } + + return try makeColor(hex: hex, alpha: alpha / 100.0) + } + + private func resolveRGBA( + red: String, + green: String, + blue: String, + alpha: String, + rgbaValue: String + ) throws -> Color { + guard + let red = Double(red), + let green = Double(green), + let blue = Double(blue), + let alpha = Double(alpha) + else { + throw TokensGeneratorError(code: .invalidRGBAColorValue(rgba: rgbaValue)) + } + + let normalizationFactor: CGFloat = 255.0 + + return Color( + red: CGFloat(red) / normalizationFactor, + green: CGFloat(green) / normalizationFactor, + blue: CGFloat(blue) / normalizationFactor, + alpha: alpha + ) + } + // MARK: - TokensResolver func resolveValue(_ value: String, tokenValues: TokenValues, theme: Theme) throws -> String { @@ -127,18 +166,28 @@ final class DefaultTokensResolver: TokensResolver { .slice(from: "(", to: ")", includingBounds: false)? .components(separatedBy: ", ") - guard let components, components.count == 2 else { + guard let components else { throw TokensGeneratorError(code: .invalidRGBAColorValue(rgba: value)) } - let hex = components[0] - let alphaPercent = components[1] - - guard let alpha = Double(alphaPercent.dropLast()) else { - throw TokensGeneratorError(code: .invalidAlphaComponent(alpha: alphaPercent)) + switch components.count { + case .rgbaWithHex: + return try resolveRGBAWithHex( + hex: components[0], + alphaPercent: components[1], + value: value + ) + case .rgba: + return try resolveRGBA( + red: components[0], + green: components[1], + blue: components[2], + alpha: components[3], + rgbaValue: value + ) + default: + throw TokensGeneratorError(code: .invalidRGBAColorValue(rgba: value)) } - - return try makeColor(hex: hex, alpha: alpha / 100.0) } func resolveHexColorValue(_ value: String, tokenValues: TokenValues, theme: Theme) throws -> String { @@ -193,4 +242,6 @@ extension Int { fileprivate static let rgb = 3 fileprivate static let rrggbb = 6 + fileprivate static let rgbaWithHex = 2 + fileprivate static let rgba = 4 } diff --git a/Sources/FigmaGen/Generators/Tokens/Resolver/TokensResolver.swift b/Sources/FigmaGen/Generators/Tokens/Resolver/TokensResolver.swift index 81f7eb0..9cf1aba 100644 --- a/Sources/FigmaGen/Generators/Tokens/Resolver/TokensResolver.swift +++ b/Sources/FigmaGen/Generators/Tokens/Resolver/TokensResolver.swift @@ -34,6 +34,7 @@ protocol TokensResolver { /// /// Supported formats: /// - `rgba(hex_color, alpha-value-percentage)` + /// – `rgba(red, green, blue, alpha-value-percentage)` /// - TO DO: Support more formats /// /// [Color tokens examples and should be supported later](https://docs.tokens.studio/available-tokens/color-tokens#solid-colors) diff --git a/Sources/FigmaGen/Models/Configuration/Tokens/TokensTemplateConfiguration.swift b/Sources/FigmaGen/Models/Configuration/Tokens/TokensTemplateConfiguration.swift index 9ceeb54..532368c 100644 --- a/Sources/FigmaGen/Models/Configuration/Tokens/TokensTemplateConfiguration.swift +++ b/Sources/FigmaGen/Models/Configuration/Tokens/TokensTemplateConfiguration.swift @@ -53,6 +53,6 @@ extension TokensTemplateConfiguration: Decodable { theme = try container.decodeIfPresent(TemplateConfigurationWrapper.self, forKey: .theme)?.templates spacing = try container.decodeIfPresent(TemplateConfigurationWrapper.self, forKey: .spacing)?.templates borders = try? container.decodeIfPresent(TemplateConfigurationWrapper.self, forKey: .borders)?.templates - gradient = try? container.decodeIfPresent(TemplateConfigurationWrapper.self, forKey: .borders)?.templates + gradient = try? container.decodeIfPresent(TemplateConfigurationWrapper.self, forKey: .gradient)?.templates } } diff --git a/Templates/GradientTokens.stencil b/Templates/GradientTokens.stencil index 17e803c..5eb6714 100644 --- a/Templates/GradientTokens.stencil +++ b/Templates/GradientTokens.stencil @@ -1 +1,47 @@ -# TODO: mi.fedorov доделать шаблон +{% include "FileHeader.stencil" %} +{% if gradients %} +{% set gradientTypeName %}{{ options.colorTypeName|default:"LinearGradient" }}{% endset %} +{% macro propertyName name %}{{ name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords }}{% endmacro %} +{% macro typeName name %}{{ name|swiftIdentifier:"pretty"|upperFirstLetter|escapeReservedKeywords }}{% endmacro %} +{% macro recursiveBlock item %} + {% for gradient in item.gradient %} + + /// {{ gradient.name }} + /// + /// Day Theme: + {% for stop in gradient.dayTheme.stops %} + /// - {{ stop.color }} {{ stop.percentage }}% + {% endfor %} + /// Night Theme: + {% for stop in gradient.nightTheme.stops %} + /// - {{ stop.color }} {{ stop.percentage }}% + {% endfor %} + public let {% call propertyName gradient.path.last %}: {{ gradientTypeName }} + {% endfor %} + {% for child in item.children %} + {% if child.name == "gradient" %} + {% call recursiveBlock child %} + {% else %} + public struct {% call typeName child.name %} { + {% filter indent:4 %} + {% call recursiveBlock child %} + {% endfilter %} + } + + public let {% call propertyName child.name %}: {% call typeName child.name %} + {% endif %} + {% endfor %} +{% endmacro %} + +import SwiftUI + +public struct Gradients { + {% if gradients %} + {% call recursiveBlock gradients %} + {% else %} + // No gradient tokens found + {% endif %} +} +{% else %} +// No color tokens found +{% endif %} From e80f6ca1c5968742bb50c180781c763b0f8d4fae Mon Sep 17 00:00:00 2001 From: nschpy Date: Fri, 28 Mar 2025 19:06:47 +0300 Subject: [PATCH 4/6] =?UTF-8?q?=D0=9D=D0=B5=D0=B1=D0=BE=D0=BB=D1=8C=D1=88?= =?UTF-8?q?=D0=B8=D0=B5=20=D1=83=D0=BB=D1=83=D1=87=D1=88=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Tokens/Contexts/LinearGradientToken.swift | 10 ++++++++-- .../DefaultGradientTokensContextProvider.swift | 12 +++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/Sources/FigmaGen/Generators/Tokens/Contexts/LinearGradientToken.swift b/Sources/FigmaGen/Generators/Tokens/Contexts/LinearGradientToken.swift index c74b046..36e0023 100644 --- a/Sources/FigmaGen/Generators/Tokens/Contexts/LinearGradientToken.swift +++ b/Sources/FigmaGen/Generators/Tokens/Contexts/LinearGradientToken.swift @@ -7,10 +7,16 @@ struct LinearGradientToken: TokenProtocol, Encodable { let percentage: CGFloat } + // Нужен, т.к CGPoint нельзя использовать корректно в stencil шаблоне + struct Point: Encodable { + let x: CGFloat + let y: CGFloat + } + struct GradientThemeValue: Encodable { let stops: [ColorStop] - let startPoint: CGPoint - let endPoint: CGPoint + let startPoint: Point + let endPoint: Point } let path: [String] diff --git a/Sources/FigmaGen/Generators/Tokens/Providers/ColorTokensContext/DefaultGradientTokensContextProvider.swift b/Sources/FigmaGen/Generators/Tokens/Providers/ColorTokensContext/DefaultGradientTokensContextProvider.swift index 62db6e8..5118544 100644 --- a/Sources/FigmaGen/Generators/Tokens/Providers/ColorTokensContext/DefaultGradientTokensContextProvider.swift +++ b/Sources/FigmaGen/Generators/Tokens/Providers/ColorTokensContext/DefaultGradientTokensContextProvider.swift @@ -59,8 +59,14 @@ struct DefaultGradientTokensContextProvider: ColorTokensContextProvider { percentage: percentage ) }, - startPoint: points.start, - endPoint: points.end + startPoint: LinearGradientToken.Point( + x: points.start.x, + y: points.start.y + ), + endPoint: LinearGradientToken.Point( + x: points.end.x, + y: points.end.y + ) ) } @@ -168,6 +174,6 @@ struct DefaultGradientTokensContextProvider: ColorTokensContextProvider { try extractGradientToken(from: $0, tokenValues: tokenValues) } - return structure(tokens: gradient, contextName: "gradient") + return structure(tokens: gradient, contextName: "gradients") } } From 82439e9e4f4c4b254825e554fce5cc910441d986 Mon Sep 17 00:00:00 2001 From: nschpy Date: Fri, 28 Mar 2025 22:26:09 +0300 Subject: [PATCH 5/6] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=B7=D0=BD=D0=B0=D1=87=D0=B5=D0=BD=D0=B8=D0=B5=20?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D1=86=D0=B5=D0=BD=D1=82=D0=BE=D0=B2=20=D0=B2?= =?UTF-8?q?=20=D1=82=D0=BE=D0=BA=D0=B5=D0=BD=D0=B5=20=D0=B3=D1=80=D0=B0?= =?UTF-8?q?=D0=B4=D0=B8=D0=B5=D0=BD=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DefaultGradientTokensContextProvider.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/FigmaGen/Generators/Tokens/Providers/ColorTokensContext/DefaultGradientTokensContextProvider.swift b/Sources/FigmaGen/Generators/Tokens/Providers/ColorTokensContext/DefaultGradientTokensContextProvider.swift index 5118544..6d8df5d 100644 --- a/Sources/FigmaGen/Generators/Tokens/Providers/ColorTokensContext/DefaultGradientTokensContextProvider.swift +++ b/Sources/FigmaGen/Generators/Tokens/Providers/ColorTokensContext/DefaultGradientTokensContextProvider.swift @@ -56,7 +56,7 @@ struct DefaultGradientTokensContextProvider: ColorTokensContextProvider { return .init( color: stop.color.hexString, - percentage: percentage + percentage: percentage / 100 ) }, startPoint: LinearGradientToken.Point( From 904e57ddad535bae0542fb4201d757f48c406cb0 Mon Sep 17 00:00:00 2001 From: nschpy Date: Fri, 28 Mar 2025 22:33:30 +0300 Subject: [PATCH 6/6] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81=20=D0=B1=D0=B0?= =?UTF-8?q?=D0=B7=D0=BE=D0=B2=D0=BE=D0=B3=D0=BE=20=D1=88=D0=B0=D0=B1=D0=BB?= =?UTF-8?q?=D0=BE=D0=BD=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Templates/GradientTokens.stencil | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Templates/GradientTokens.stencil b/Templates/GradientTokens.stencil index 5eb6714..8f1a445 100644 --- a/Templates/GradientTokens.stencil +++ b/Templates/GradientTokens.stencil @@ -4,7 +4,7 @@ {% macro propertyName name %}{{ name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords }}{% endmacro %} {% macro typeName name %}{{ name|swiftIdentifier:"pretty"|upperFirstLetter|escapeReservedKeywords }}{% endmacro %} {% macro recursiveBlock item %} - {% for gradient in item.gradient %} + {% for gradient in item.gradients %} /// {{ gradient.name }} /// @@ -36,11 +36,7 @@ import SwiftUI public struct Gradients { - {% if gradients %} {% call recursiveBlock gradients %} - {% else %} - // No gradient tokens found - {% endif %} } {% else %} // No color tokens found