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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions Sources/FigmaGen/Commands/ImagesCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,9 @@ final class ImagesCommand: AsyncExecutableCommand, GenerationConfigurableCommand
let sfSymbolKey = Key<String>(
"--sfSymbolKey",
description: """
Colored icons flag name from Figma.
By default, assets will be generated without processing colored info.
Name of the Figma property that marks SF Symbols: components named with '<key>=true'
(case-insensitive) are rendered as SVG and saved to .symbolset instead of .imageset.
By default, no SF Symbols are generated.
"""
)

Expand All @@ -209,6 +210,14 @@ final class ImagesCommand: AsyncExecutableCommand, GenerationConfigurableCommand
"""
)

let symbolLayersName = Key<String>(
"--symbolLayersName",
description: """
Names for primary, secondary and tertiary layers separated by comma.
By default use black color for primary and other - for secondary, tertiary ignored.
"""
)

let sfSymbolTemplate = Key<String>(
"--sfSymbolTemplate",
description: """
Expand Down Expand Up @@ -273,7 +282,7 @@ final class ImagesCommand: AsyncExecutableCommand, GenerationConfigurableCommand

case let rawSymbolRenderingMode?:
guard let mode = SymbolRenderingMode(rawValue: rawSymbolRenderingMode) else {
fail(message: "Failed to generate symbols: Invalid rendering mode (\(rawSymbolRenderingMode)")
fail(message: "Failed to generate symbols: Invalid rendering mode (\(rawSymbolRenderingMode))")
}

return mode
Expand Down Expand Up @@ -312,7 +321,8 @@ final class ImagesCommand: AsyncExecutableCommand, GenerationConfigurableCommand
namingStyle: resolveNamingStyle(),
sfSymbolKey: sfSymbolKey.value,
symbolRenderAs: resolveSymbolRenderAs(),
sfSymbolTemplate: sfSymbolTemplate.value
sfSymbolTemplate: sfSymbolTemplate.value,
symbolLayersName: symbolLayersName.value
)
}

Expand Down
1 change: 1 addition & 0 deletions Sources/FigmaGen/Dependencies.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ enum Dependencies {
static let dataProvider: DataProvider = DefaultDataProvider()
static let svgParser: SVGParser = DefaultSVGParser()
static let sfSymbolProvider: SFSymbolProvider = DefaultSFSymbolProvider(
dataProvider: dataProvider,
svgParser: svgParser,
templateRenderer: templateRenderer
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ extension ImagesConfiguration {
namingStyle: namingStyle,
sfSymbolKey: sfSymbolKey,
symbolRenderAs: symbolRenderAs,
sfSymbolTemplate: sfSymbolTemplate
sfSymbolTemplate: sfSymbolTemplate,
symbolLayersName: symbolLayersName
)
}
}
10 changes: 8 additions & 2 deletions Sources/FigmaGen/Models/Configuration/ImagesConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ struct ImagesConfiguration: Decodable {
case sfSymbolKey
case symbolRenderAs
case sfSymbolTemplate
case symbolLayersName
}

// MARK: - Instance Properties
Expand All @@ -42,6 +43,7 @@ struct ImagesConfiguration: Decodable {
let sfSymbolKey: String?
let symbolRenderAs: SymbolRenderingMode?
let sfSymbolTemplate: String?
let symbolLayersName: String?

// MARK: - Initializers

Expand All @@ -62,7 +64,8 @@ struct ImagesConfiguration: Decodable {
namingStyle: ImageNamingStyle,
sfSymbolKey: String?,
symbolRenderAs: SymbolRenderingMode?,
sfSymbolTemplate: String?
sfSymbolTemplate: String?,
symbolLayersName: String?
) {
self.generatation = generatation
self.assets = assets
Expand All @@ -81,6 +84,7 @@ struct ImagesConfiguration: Decodable {
self.sfSymbolKey = sfSymbolKey
self.symbolRenderAs = symbolRenderAs
self.sfSymbolTemplate = sfSymbolTemplate
self.symbolLayersName = symbolLayersName
}

init(from decoder: Decoder) throws {
Expand All @@ -103,6 +107,7 @@ struct ImagesConfiguration: Decodable {
sfSymbolKey = try container.decodeIfPresent(forKey: .sfSymbolKey)
symbolRenderAs = try container.decodeIfPresent(forKey: .symbolRenderAs)
sfSymbolTemplate = try container.decodeIfPresent(forKey: .sfSymbolTemplate)
symbolLayersName = try container.decodeIfPresent(forKey: .symbolLayersName)

generatation = try GenerationConfiguration(from: decoder)
}
Expand All @@ -127,7 +132,8 @@ struct ImagesConfiguration: Decodable {
namingStyle: namingStyle,
sfSymbolKey: sfSymbolKey,
symbolRenderAs: symbolRenderAs,
sfSymbolTemplate: sfSymbolTemplate
sfSymbolTemplate: sfSymbolTemplate,
symbolLayersName: symbolLayersName
)
}
}
6 changes: 6 additions & 0 deletions Sources/FigmaGen/Models/Images/ImageNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,10 @@ struct ImageNode: Encodable, Hashable {
let id: String
let name: String
let description: String?

// MARK: - Instance Methods

func isSFSymbol(key: String?) -> Bool {
key.map { name.lowercased().contains("\($0.lowercased())=true") } ?? false
}
}
1 change: 1 addition & 0 deletions Sources/FigmaGen/Models/Parameters/ImagesParameters.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ struct ImagesParameters {
let sfSymbolKey: String?
let symbolRenderAs: SymbolRenderingMode?
let sfSymbolTemplate: String?
let symbolLayersName: String?
}
11 changes: 11 additions & 0 deletions Sources/FigmaGen/Models/SFSymbol/SFSymbol.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Foundation

struct SFSymbol {

let name: String
let opticalWidth: CGFloat
let opticalHeight: CGFloat
let designWidth: CGFloat
let designHeight: CGFloat
let layers: [SFSymbolLayer]
}
3 changes: 1 addition & 2 deletions Sources/FigmaGen/Models/SFSymbol/SFSymbolLayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import Foundation

struct SFSymbolLayer {

let index: Int
let role: SFSymbolRole
let paths: [SFSymbolPathData]
let paths: [SFSymbolPath]
}
7 changes: 7 additions & 0 deletions Sources/FigmaGen/Models/SFSymbol/SFSymbolPath.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Foundation

struct SFSymbolPath {

let data: String
let fillRule: String?
}
8 changes: 0 additions & 8 deletions Sources/FigmaGen/Models/SFSymbol/SFSymbolPathData.swift

This file was deleted.

18 changes: 16 additions & 2 deletions Sources/FigmaGen/Models/SFSymbol/SFSymbolRole.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
import Foundation

/// Роль пути в палитре слоёв. Определяется по id пути, а если его нет - по заливке:
/// чёрная заливка из Figma даёт primary, любая другая - secondary.
enum SFSymbolRole: String {

case primary
case secondary
case tertiary

// MARK: - Initializers

init(of path: SVGPath, layerNames: [String]) {
if let ownRole = path.id.flatMap({ Self(rawValue: $0.lowercased()) }) {
self = ownRole
} else if layerNames.isEmpty {
self = path.fill == .black ? .primary : .secondary
} else if path.idChain.contains(layerNames[0]) {
self = .primary
} else if layerNames.count > 1, path.idChain.contains(layerNames[1]) {
self = .secondary
} else {
self = .tertiary
}
}
}
7 changes: 7 additions & 0 deletions Sources/FigmaGen/Models/SVG/SVGDocument.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Foundation

struct SVGDocument {

let canvas: SVGCanvas?
let paths: [SVGPath]
}
8 changes: 0 additions & 8 deletions Sources/FigmaGen/Models/SVG/SVGGroupContext.swift

This file was deleted.

11 changes: 0 additions & 11 deletions Sources/FigmaGen/Models/SVG/SVGImageToken.swift

This file was deleted.

46 changes: 12 additions & 34 deletions Sources/FigmaGen/Models/SVG/SVGPath.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,21 @@ import Foundation

struct SVGPath {

let attributes: [String: String]
let groupIDs: [String]
let inheritedTransforms: [String]
let fill: SVGColor?

var id: String? {
attributes["id"]
}

var wholeID: String {
(groupIDs + [attributes["id"]].compactMap { $0 })
.joined(separator: ";")
}
// MARK: - Instance Properties

var data: String? {
attributes["d"]
}

var fillRule: String? {
attributes["fill-rule"]
}
let id: String?
let data: String?
let fillRule: String?
let fill: SVGColor?
let transform: String?
let groupIDs: [String]
let groupTransforms: [String]

var allTransforms: [String] {
inheritedTransforms + [attributes["transform"]].compactMap { $0 }
var idChain: String {
(groupIDs + [id].compactMap { $0 }).joined(separator: ";")
}

// MARK: - Initializers

init(
attributes: [String: String],
groupIDs: [String],
inheritedTransforms: [String],
fill: SVGColor?
) {
self.attributes = attributes
self.groupIDs = groupIDs
self.inheritedTransforms = inheritedTransforms
self.fill = fill
var transformChain: [String] {
groupTransforms + [transform].compactMap { $0 }
}
}
14 changes: 0 additions & 14 deletions Sources/FigmaGen/Models/SVG/SVGPathsResult.swift

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,12 @@ final class DefaultImageAssetsProvider: ImageAssetsProvider, ImagesFolderPathRes
) -> String {
var name = setNode.type == .component ? node.base.name : "\(setNode.name) \(node.base.name)"

if
let sfSymbolKey,
!sfSymbolKey.isEmpty,
name.lowercased().contains(sfSymbolKey.lowercased()) {
if let sfSymbolKey, !sfSymbolKey.isEmpty {
name = name
.replacingOccurrences(of: "\(sfSymbolKey)=false", with: "")
.replacingOccurrences(of: "\(sfSymbolKey)=true", with: "\(sfSymbolKey)")
.replacingOccurrences(of: "\(sfSymbolKey)=false", with: "", options: .caseInsensitive)
.replacingOccurrences(of: "\(sfSymbolKey)=true", with: sfSymbolKey, options: .caseInsensitive)
}

switch namingStyle {
case .camelCase:
return name.camelized
Expand Down Expand Up @@ -70,24 +68,27 @@ final class DefaultImageAssetsProvider: ImageAssetsProvider, ImagesFolderPathRes
folderPath: folderPath
)

let isSymbol = parameters.sfSymbolKey != nil
&& name.lowercased().contains(parameters.sfSymbolKey?.lowercased() ?? "")

let assetSetExtension = isSymbol
? AssetSymbolSet.pathExtension
: AssetImageSet.pathExtension

let assetExtension = isSymbol
? ImageFormat.svg.fileExtension
: parameters.format.fileExtension

let symbolRenderingMode = isSymbol ? parameters.symbolRenderAs : nil

let filePaths = node.urls.keys.reduce(into: [:]) { result, scale in
result[isSymbol ? .none : scale] = folderPath
.appending(fileName: name, extension: assetSetExtension)
.appending(fileName: name.appending(scale.fileNameSuffix), extension: assetExtension)
.string
let isSymbol = node.base.isSFSymbol(key: parameters.sfSymbolKey)

let filePaths: [ImageScale: String]

if isSymbol {
filePaths = [
.none: folderPath
.appending(fileName: name, extension: AssetSymbolSet.pathExtension)
.appending(fileName: name, extension: ImageFormat.svg.fileExtension)
.string
]
} else {
filePaths = node.urls.keys.reduce(into: [:]) { result, scale in
result[scale] = folderPath
.appending(fileName: name, extension: AssetImageSet.pathExtension)
.appending(
fileName: name.appending(scale.fileNameSuffix),
extension: parameters.format.fileExtension
)
.string
}
}

return ImageAsset(
Expand All @@ -96,7 +97,7 @@ final class DefaultImageAssetsProvider: ImageAssetsProvider, ImagesFolderPathRes
preserveVectorData: parameters.preserveVectorData,
renderAs: parameters.renderAs,
isSymbol: isSymbol,
symbolRenderAs: symbolRenderingMode
symbolRenderAs: isSymbol ? parameters.symbolRenderAs : nil
)
}

Expand Down Expand Up @@ -184,12 +185,8 @@ final class DefaultImageAssetsProvider: ImageAssetsProvider, ImagesFolderPathRes
parameters: ImagesParameters
) -> Promise<Void> {
let promises = node.urls.compactMap { scale, url in
asset.filePaths[scale].map {
self.sfSymbolProvider.saveData(
from: url,
to: $0,
template: parameters.sfSymbolTemplate
)
asset.filePaths[scale].map { filePath in
self.sfSymbolProvider.saveSymbol(from: url, to: filePath, parameters: parameters)
}
}

Expand Down
Loading