admiral-tools-figma 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +1 -0
- data/admiral-tools-figma.gemspec +9 -0
- data/admiraltools/actions/figma_download_images.rb +116 -0
- data/admiraltools/actions/figma_generate_styles.rb +77 -0
- data/admiraltools/actions/figma_get_image_links.rb +139 -0
- data/admiraltools/actions/figma_get_styles.rb +90 -0
- data/admiraltools/helper/admiraltools_helper.rb +18 -0
- data/admiraltools/helper/figma/figma_client/figma_client.rb +193 -0
- data/admiraltools/helper/figma/figma_client/models/api/primitives/figma_color.rb +35 -0
- data/admiraltools/helper/figma/figma_client/models/api/primitives/figma_component.rb +58 -0
- data/admiraltools/helper/figma/figma_client/models/api/primitives/figma_components_styles_meta.rb +30 -0
- data/admiraltools/helper/figma/figma_client/models/api/primitives/figma_frame_info.rb +46 -0
- data/admiraltools/helper/figma/figma_client/models/api/primitives/figma_node.rb +43 -0
- data/admiraltools/helper/figma/figma_client/models/api/primitives/figma_paint.rb +32 -0
- data/admiraltools/helper/figma/figma_client/models/api/primitives/figma_style.rb +48 -0
- data/admiraltools/helper/figma/figma_client/models/api/primitives/figma_type_style.rb +56 -0
- data/admiraltools/helper/figma/figma_client/models/api/results/figma_components_styles_result.rb +36 -0
- data/admiraltools/helper/figma/figma_client/models/api/results/figma_images_result.rb +25 -0
- data/admiraltools/helper/figma/figma_client/models/api/results/figma_nodes_result.rb +52 -0
- data/admiraltools/helper/figma/figma_client/models/domain/converters/figma_to_domain_converter.rb +98 -0
- data/admiraltools/helper/figma/figma_client/models/domain/primitives/color.rb +44 -0
- data/admiraltools/helper/figma/figma_client/models/domain/primitives/component.rb +86 -0
- data/admiraltools/helper/figma/figma_client/models/domain/primitives/components_list.rb +29 -0
- data/admiraltools/helper/figma/figma_client/models/domain/primitives/font.rb +56 -0
- data/admiraltools/helper/figma/figma_client/models/domain/primitives/frame_info.rb +46 -0
- data/admiraltools/helper/figma/figma_client/models/domain/primitives/image_link.rb +33 -0
- data/admiraltools/helper/figma/figma_client/models/domain/primitives/style.rb +113 -0
- data/admiraltools/helper/figma/figma_client/models/domain/primitives/styles_group.rb +22 -0
- data/admiraltools/helper/figma/figma_client/models/domain/primitives/styles_list.rb +29 -0
- data/admiraltools/helper/figma/figma_client/models/models.rb +25 -0
- data/admiraltools/helper/figma/figma_image_downloader/bin/svg2vectordrawable.jar +0 -0
- data/admiraltools/helper/figma/figma_image_downloader/figma_image_downloader.rb +65 -0
- data/admiraltools/helper/figma/figma_image_downloader/generators/folder_generator.rb +47 -0
- data/admiraltools/helper/figma/figma_image_downloader/generators/folder_generator_raw.rb +32 -0
- data/admiraltools/helper/figma/figma_image_downloader/generators/folder_generator_res.rb +38 -0
- data/admiraltools/helper/figma/figma_image_downloader/generators/folder_generator_xcassets.rb +180 -0
- data/admiraltools/helper/figma/figma_image_downloader/generators/generators.rb +6 -0
- data/admiraltools/helper/figma/figma_image_downloader/helpers/image_name_formatter.rb +72 -0
- data/admiraltools/helper/figma/figma_image_downloader/helpers/progress_tasks_logger.rb +22 -0
- data/admiraltools/helper/figma/figma_image_downloader/helpers/scale_formatter.rb +96 -0
- data/admiraltools/helper/figma/figma_image_downloader/helpers/svg_to_vector_converter.rb +12 -0
- data/admiraltools/helper/figma/figma_image_downloader/models/download_image_params.rb +31 -0
- data/admiraltools/helper/figma/figma_image_downloader/models/image_name_components.rb +10 -0
- data/admiraltools/helper/figma/figma_image_downloader/models/image_ref.rb +12 -0
- data/admiraltools/helper/figma/figma_image_downloader/models/image_set_ref.rb +14 -0
- data/admiraltools/helper/figma/figma_image_downloader/models/models.rb +5 -0
- data/admiraltools/helper/figma/figma_template_generator/figma_template_generator.rb +22 -0
- data/admiraltools/helper/figma/figma_template_generator/models/styles_template.rb +34 -0
- data/admiraltools/helper/figma/helpers/file_folder.rb +30 -0
- data/admiraltools/helper/figma/helpers/hash_access.rb +18 -0
- data/admiraltools/helper/figma/helpers/string_filters.rb +63 -0
- data/admiraltools/helper/figma/helpers/string_format.rb +60 -0
- data/admiraltools/helper/system/system.rb +17 -0
- data/admiraltools/version.rb +7 -0
- data/admiraltools.rb +18 -0
- metadata +96 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: db6a98e25fe4de8274065f26dfc4db1808034dfe4b60b80d40c9f99d383729f7
|
4
|
+
data.tar.gz: 8b204394e797d86d8ac6de7ace055ece02631b879b30457cf1ed43aa0e11fe72
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 34fc5807ebeb31cc95e1797e535039dd3aafa58c051b3c628f70a12faf43cbcf869888f3afcad60746a4fd746f9cbd36f377ee2d91a89d6ebc8863a7bbed050b
|
7
|
+
data.tar.gz: 389fc58b3afa3584df804e769e3ccee55823a594300522fe1dc0e1c3f001f2e91d70343bb81da95066dac45e78a823d98af879876611bb926894ddf35049ae64
|
data/README.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# admiral-tools-figma
|
@@ -0,0 +1,9 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "admiral-tools-figma"
|
3
|
+
s.authors = ["Admiral team"]
|
4
|
+
s.version = "0.0.1"
|
5
|
+
s.homepage = "https://github.com/admiral-team"
|
6
|
+
s.description = "The tool to download figma icons"
|
7
|
+
s.files = Dir['**/*']
|
8
|
+
s.summary = "The tool to download figma icons"
|
9
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fastlane/action'
|
4
|
+
require_relative '../helper/admiraltools_helper'
|
5
|
+
require_relative '../helper/figma/figma_image_downloader/figma_image_downloader'
|
6
|
+
|
7
|
+
module Fastlane
|
8
|
+
module Actions
|
9
|
+
class FigmaDownloadImagesAction < Action
|
10
|
+
def self.run(params)
|
11
|
+
image_components_json = params[:image_components]
|
12
|
+
output_folder = params[:output_folder]
|
13
|
+
output_format = params[:output_format]
|
14
|
+
folder_depth = params[:folder_depth]
|
15
|
+
naming_style = params[:naming_style]
|
16
|
+
xcassets_params = params[:xcassets_params]
|
17
|
+
convert_svg_to_vector = params[:convert_svg_to_vector]
|
18
|
+
convert_scales_to_dpi = params[:convert_scales_to_dpi]
|
19
|
+
include_page_name = params[:include_page_name]
|
20
|
+
include_frame_name = params[:include_frame_name]
|
21
|
+
|
22
|
+
image_components = ComponentsList.from_json(image_components_json).components || []
|
23
|
+
downloader = FigmaImageDownloader.new
|
24
|
+
|
25
|
+
image_params = DownloadImageParams.new(
|
26
|
+
image_components: image_components,
|
27
|
+
output_folder: output_folder,
|
28
|
+
folder_depth: folder_depth,
|
29
|
+
output_format: output_format,
|
30
|
+
naming_style: naming_style,
|
31
|
+
xcassets_params: xcassets_params,
|
32
|
+
convert_svg_to_vector: convert_svg_to_vector,
|
33
|
+
convert_scales_to_dpi: convert_scales_to_dpi,
|
34
|
+
include_page_name: include_page_name,
|
35
|
+
include_frame_name: include_frame_name
|
36
|
+
)
|
37
|
+
|
38
|
+
downloader.download_images(params: image_params)
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.description
|
42
|
+
'Figma plugin'
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.authors
|
46
|
+
['ton252']
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.return_value
|
50
|
+
# If your method provides a return value, you can describe here what it does
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.details
|
54
|
+
'Figma plugin'
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.available_options
|
58
|
+
[
|
59
|
+
FastlaneCore::ConfigItem.new(key: :image_components,
|
60
|
+
description: 'Image components json',
|
61
|
+
optional: false,
|
62
|
+
type: String),
|
63
|
+
FastlaneCore::ConfigItem.new(key: :output_folder,
|
64
|
+
description: 'Output folder path',
|
65
|
+
optional: false,
|
66
|
+
type: String),
|
67
|
+
FastlaneCore::ConfigItem.new(key: :output_format,
|
68
|
+
description: "Output type: #{FigmaImageDownloader::OUTPUT_FORMAT_XCASSETS}, #{FigmaImageDownloader::OUTPUT_FORMAT_RES}, #{FigmaImageDownloader::OUTPUT_FORMAT_RAW}",
|
69
|
+
optional: false,
|
70
|
+
type: String),
|
71
|
+
FastlaneCore::ConfigItem.new(key: :folder_depth,
|
72
|
+
description: 'Folder depth',
|
73
|
+
optional: true,
|
74
|
+
default_value: -1,
|
75
|
+
type: Integer),
|
76
|
+
FastlaneCore::ConfigItem.new(key: :naming_style,
|
77
|
+
description: "Image naming style: #{String::FORMAT_STYLE_CAMELCASE}, #{String::FORMAT_STYLE_SNAKECASE}, #{String::FORMAT_STYLE_KEBABCASE}",
|
78
|
+
optional: true,
|
79
|
+
type: String),
|
80
|
+
FastlaneCore::ConfigItem.new(key: :xcassets_params,
|
81
|
+
description: 'Xcassets parameters (template_rendering_intent, provides-namespace, preserves_vector_representation)',
|
82
|
+
optional: true,
|
83
|
+
type: Hash),
|
84
|
+
FastlaneCore::ConfigItem.new(key: :convert_svg_to_vector,
|
85
|
+
description: 'Convert svg images to Android vector',
|
86
|
+
optional: true,
|
87
|
+
default_value: false,
|
88
|
+
is_string: false),
|
89
|
+
FastlaneCore::ConfigItem.new(key: :convert_scales_to_dpi,
|
90
|
+
description: 'Convert scales to dpi',
|
91
|
+
optional: true,
|
92
|
+
default_value: false,
|
93
|
+
is_string: false),
|
94
|
+
FastlaneCore::ConfigItem.new(key: :include_page_name,
|
95
|
+
description: 'Include page name',
|
96
|
+
optional: true,
|
97
|
+
default_value: false,
|
98
|
+
is_string: false),
|
99
|
+
FastlaneCore::ConfigItem.new(key: :include_frame_name,
|
100
|
+
description: 'Include page name',
|
101
|
+
optional: true,
|
102
|
+
default_value: false,
|
103
|
+
is_string: false)
|
104
|
+
]
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.is_supported?(_platform)
|
108
|
+
# Adjust this if your plugin only works for a particular platform (iOS vs. Android, for example)
|
109
|
+
# See: https://docs.fastlane.tools/advanced/#control-configuration-by-lane-and-by-platform
|
110
|
+
#
|
111
|
+
# [:ios, :mac, :android].include?(platform)
|
112
|
+
true
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'fastlane/action'
|
5
|
+
require_relative '../helper/admiraltools_helper'
|
6
|
+
require_relative '../helper/figma/figma_template_generator/figma_template_generator'
|
7
|
+
|
8
|
+
module Fastlane
|
9
|
+
module Actions
|
10
|
+
class FigmaGenerateStylesAction < Action
|
11
|
+
def self.run(params)
|
12
|
+
Actions.verify_gem!('liquid')
|
13
|
+
|
14
|
+
styles_json = params[:styles]
|
15
|
+
style_types = params[:style_types]
|
16
|
+
template_file = params[:template_file]
|
17
|
+
output_file = params[:output_file]
|
18
|
+
|
19
|
+
generator = FigmaTemplateGenerator.new
|
20
|
+
styles = StylesList.from_json(styles_json).styles
|
21
|
+
|
22
|
+
generator.generate(
|
23
|
+
styles: styles,
|
24
|
+
style_types: style_types,
|
25
|
+
template_path: template_file,
|
26
|
+
output_path: output_file
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.description
|
31
|
+
'Figma plugin'
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.authors
|
35
|
+
['ton252']
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.return_value
|
39
|
+
# If your method provides a return value, you can describe here what it does
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.details
|
43
|
+
'Figma plugin'
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.available_options
|
47
|
+
[
|
48
|
+
FastlaneCore::ConfigItem.new(key: :styles,
|
49
|
+
description: 'Figma styles list json',
|
50
|
+
optional: false,
|
51
|
+
type: String),
|
52
|
+
FastlaneCore::ConfigItem.new(key: :template_file,
|
53
|
+
description: 'Figma template liquid file path',
|
54
|
+
optional: false,
|
55
|
+
type: String),
|
56
|
+
FastlaneCore::ConfigItem.new(key: :output_file,
|
57
|
+
description: 'Figma output styles file path',
|
58
|
+
optional: false,
|
59
|
+
type: String),
|
60
|
+
FastlaneCore::ConfigItem.new(key: :style_types,
|
61
|
+
description: 'Figma style type filter [FILL, TEXT, EFFECT, GRID]',
|
62
|
+
optional: true,
|
63
|
+
type: Array)
|
64
|
+
|
65
|
+
]
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.is_supported?(_platform)
|
69
|
+
# Adjust this if your plugin only works for a particular platform (iOS vs. Android, for example)
|
70
|
+
# See: https://docs.fastlane.tools/advanced/#control-configuration-by-lane-and-by-platform
|
71
|
+
#
|
72
|
+
# [:ios, :mac, :android].include?(platform)
|
73
|
+
true
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fastlane/action'
|
4
|
+
require_relative '../helper/admiraltools_helper'
|
5
|
+
require_relative '../helper/figma/figma_client/figma_client'
|
6
|
+
|
7
|
+
module Fastlane
|
8
|
+
module Actions
|
9
|
+
class FigmaGetImageLinksAction < Action
|
10
|
+
def self.run(params)
|
11
|
+
access_token = params[:access_token]
|
12
|
+
file_key = params[:file_key]
|
13
|
+
name_filter_regex = params[:name_filter_regex]
|
14
|
+
description_filter_regex = params[:description_filter_regex]
|
15
|
+
frame_filter_regex = params[:frame_filter_regex]
|
16
|
+
page_filter_regex = params[:page_filter_regex]
|
17
|
+
components_output_file = params[:output_file]
|
18
|
+
image_format = params[:format]
|
19
|
+
image_scales = params[:scales]
|
20
|
+
svg_include_id = params[:svg_include_id]
|
21
|
+
svg_simplify_stroke = params[:svg_simplify_stroke]
|
22
|
+
use_absolute_bounds = params[:use_absolute_bounds]
|
23
|
+
request_batch = params[:request_batch]
|
24
|
+
scales = ScaleFormatter.new.any_scales_to_scales(image_scales)
|
25
|
+
|
26
|
+
client = FigmaClient.new(access_token: access_token)
|
27
|
+
|
28
|
+
components_list = client.get_full_image_components(
|
29
|
+
file_key: file_key,
|
30
|
+
image_format: image_format,
|
31
|
+
scales: scales,
|
32
|
+
name_filter_regex: name_filter_regex,
|
33
|
+
desc_filter_regex: description_filter_regex,
|
34
|
+
frame_filter_regex: frame_filter_regex,
|
35
|
+
page_filter_regex: page_filter_regex,
|
36
|
+
svg_include_id: svg_include_id,
|
37
|
+
svg_simplify_stroke: svg_simplify_stroke,
|
38
|
+
use_absolute_bounds: use_absolute_bounds,
|
39
|
+
request_batch: request_batch
|
40
|
+
)
|
41
|
+
|
42
|
+
unless components_output_file.nil?
|
43
|
+
File.write_file_json(
|
44
|
+
hash: components_list.to_hash,
|
45
|
+
path: components_output_file,
|
46
|
+
create_directories: true
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
components_list
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.description
|
54
|
+
'Figma plugin'
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.authors
|
58
|
+
['ton252']
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.return_value
|
62
|
+
# If your method provides a return value, you can describe here what it does
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.details
|
66
|
+
'Figma plugin'
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.available_options
|
70
|
+
[
|
71
|
+
FastlaneCore::ConfigItem.new(key: :access_token,
|
72
|
+
description: 'Figma access token',
|
73
|
+
env_name: 'FIGMA_ACCESS_TOKEN',
|
74
|
+
optional: false,
|
75
|
+
type: String),
|
76
|
+
FastlaneCore::ConfigItem.new(key: :file_key,
|
77
|
+
env_name: 'FIGMA_FILE_KEY',
|
78
|
+
description: 'Figma file key',
|
79
|
+
optional: false,
|
80
|
+
type: String),
|
81
|
+
FastlaneCore::ConfigItem.new(key: :name_filter_regex,
|
82
|
+
description: 'Name filter regex',
|
83
|
+
optional: true,
|
84
|
+
type: String),
|
85
|
+
FastlaneCore::ConfigItem.new(key: :description_filter_regex,
|
86
|
+
description: 'Description filter regex',
|
87
|
+
optional: true,
|
88
|
+
type: String),
|
89
|
+
FastlaneCore::ConfigItem.new(key: :frame_filter_regex,
|
90
|
+
description: 'Frame filter regex',
|
91
|
+
optional: true,
|
92
|
+
type: String),
|
93
|
+
FastlaneCore::ConfigItem.new(key: :page_filter_regex,
|
94
|
+
description: 'Page filter regex',
|
95
|
+
optional: true,
|
96
|
+
type: String),
|
97
|
+
FastlaneCore::ConfigItem.new(key: :format,
|
98
|
+
description: 'Image output format, can be [jpg png svg pdf]',
|
99
|
+
optional: true,
|
100
|
+
type: String),
|
101
|
+
FastlaneCore::ConfigItem.new(key: :scales,
|
102
|
+
description: 'An array of number between 0.01 and 4, the image scaling factor. Example: ["0.5", "1", "2", "3"]',
|
103
|
+
optional: true,
|
104
|
+
type: Array),
|
105
|
+
FastlaneCore::ConfigItem.new(key: :request_batch,
|
106
|
+
description: 'Figma max request components batch',
|
107
|
+
default_value: 1_000_000,
|
108
|
+
optional: true,
|
109
|
+
type: Integer),
|
110
|
+
FastlaneCore::ConfigItem.new(key: :output_file,
|
111
|
+
description: 'Figma output component file path',
|
112
|
+
optional: true,
|
113
|
+
type: String),
|
114
|
+
FastlaneCore::ConfigItem.new(key: :svg_include_id,
|
115
|
+
description: 'Whether to include id attributes for all SVG elements. Default: false',
|
116
|
+
optional: true,
|
117
|
+
type: Boolean),
|
118
|
+
FastlaneCore::ConfigItem.new(key: :svg_simplify_stroke,
|
119
|
+
description: 'Whether to simplify inside/outside strokes and use stroke attribute if possible instead of <mask>. Default: true',
|
120
|
+
optional: true,
|
121
|
+
type: Boolean),
|
122
|
+
FastlaneCore::ConfigItem.new(key: :use_absolute_bounds,
|
123
|
+
description: 'If image use its bounds box',
|
124
|
+
default_value: true,
|
125
|
+
optional: true,
|
126
|
+
type: Boolean)
|
127
|
+
]
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.is_supported?(_platform)
|
131
|
+
# Adjust this if your plugin only works for a particular platform (iOS vs. Android, for example)
|
132
|
+
# See: https://docs.fastlane.tools/advanced/#control-configuration-by-lane-and-by-platform
|
133
|
+
#
|
134
|
+
# [:ios, :mac, :android].include?(platform)
|
135
|
+
true
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'fastlane/action'
|
5
|
+
require_relative '../helper/admiraltools_helper'
|
6
|
+
require_relative '../helper/figma/figma_client/figma_client'
|
7
|
+
|
8
|
+
module Fastlane
|
9
|
+
module Actions
|
10
|
+
class FigmaGetStylesAction < Action
|
11
|
+
def self.run(params)
|
12
|
+
Actions.verify_gem!('liquid')
|
13
|
+
|
14
|
+
access_token = params[:access_token]
|
15
|
+
file_key = params[:file_key]
|
16
|
+
styles_output_file = params[:output_file]
|
17
|
+
style_types = params[:style_types]
|
18
|
+
request_batch = params[:request_batch]
|
19
|
+
|
20
|
+
client = FigmaClient.new(access_token: access_token)
|
21
|
+
styles = client.get_full_styles(file_key: file_key, request_batch: request_batch) || []
|
22
|
+
styles = styles.select { |s| (style_types.nil? || style_types.include?(s.style_type)) }
|
23
|
+
styles_list = StylesList.new(styles: styles)
|
24
|
+
|
25
|
+
unless styles_output_file.nil?
|
26
|
+
File.write_file_json(
|
27
|
+
hash: styles_list.to_hash,
|
28
|
+
path: styles_output_file,
|
29
|
+
create_directories: true
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
styles_list
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.description
|
37
|
+
'Figma plugin'
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.authors
|
41
|
+
['ton252']
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.return_value
|
45
|
+
# If your method provides a return value, you can describe here what it does
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.details
|
49
|
+
'Figma plugin'
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.available_options
|
53
|
+
[
|
54
|
+
FastlaneCore::ConfigItem.new(key: :access_token,
|
55
|
+
description: 'Figma access token',
|
56
|
+
env_name: 'FIGMA_ACCESS_TOKEN',
|
57
|
+
optional: false,
|
58
|
+
type: String),
|
59
|
+
FastlaneCore::ConfigItem.new(key: :file_key,
|
60
|
+
env_name: 'FIGMA_FILE_KEY',
|
61
|
+
description: 'Figma file key',
|
62
|
+
optional: false,
|
63
|
+
type: String),
|
64
|
+
FastlaneCore::ConfigItem.new(key: :style_types,
|
65
|
+
description: 'Figma style type filter [FILL, TEXT, EFFECT, GRID]',
|
66
|
+
optional: true,
|
67
|
+
type: Array),
|
68
|
+
FastlaneCore::ConfigItem.new(key: :request_batch,
|
69
|
+
description: 'Figma max request components batch',
|
70
|
+
default_value: 1_000_000,
|
71
|
+
optional: true,
|
72
|
+
type: Integer),
|
73
|
+
FastlaneCore::ConfigItem.new(key: :output_file,
|
74
|
+
description: 'Figma output styles file path',
|
75
|
+
optional: true,
|
76
|
+
type: String)
|
77
|
+
|
78
|
+
]
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.is_supported?(_platform)
|
82
|
+
# Adjust this if your plugin only works for a particular platform (iOS vs. Android, for example)
|
83
|
+
# See: https://docs.fastlane.tools/advanced/#control-configuration-by-lane-and-by-platform
|
84
|
+
#
|
85
|
+
# [:ios, :mac, :android].include?(platform)
|
86
|
+
true
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fastlane_core/ui/ui'
|
4
|
+
|
5
|
+
module Fastlane
|
6
|
+
UI = FastlaneCore::UI unless Fastlane.const_defined?('UI')
|
7
|
+
|
8
|
+
module Helper
|
9
|
+
class AdmiraltoolsHelper
|
10
|
+
# class methods that you define here become available in your action
|
11
|
+
# as `Helper::AdmiraltoolsHelper.your_method`
|
12
|
+
#
|
13
|
+
def self.show_message
|
14
|
+
UI.message('Hello from the admiraltools plugin helpers!')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'uri'
|
4
|
+
require 'json'
|
5
|
+
require 'net/http'
|
6
|
+
require_relative 'models/models'
|
7
|
+
|
8
|
+
class FigmaClient
|
9
|
+
TOKEN_HEADER_NAME = 'X-FIGMA-TOKEN'
|
10
|
+
CONTENT_TYPE_HEADER_NAME = 'Content-Type'
|
11
|
+
CONTENT_TYPE_HEADER_VALUE = 'application/json'
|
12
|
+
BASE_URL_DEFAULT = 'https://api.figma.com/v1'
|
13
|
+
|
14
|
+
def initialize(access_token:, base_url: nil)
|
15
|
+
@access_token = access_token
|
16
|
+
@base_url = base_url || BASE_URL_DEFAULT
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_full_image_components(
|
20
|
+
file_key:,
|
21
|
+
image_format:,
|
22
|
+
scales:,
|
23
|
+
name_filter_regex:,
|
24
|
+
desc_filter_regex: nil,
|
25
|
+
frame_filter_regex: nil,
|
26
|
+
page_filter_regex: nil,
|
27
|
+
request_batch: nil,
|
28
|
+
svg_include_id: nil,
|
29
|
+
svg_simplify_stroke: nil,
|
30
|
+
use_absolute_bounds: nil
|
31
|
+
)
|
32
|
+
converter = FigmaToDomainConverter.new
|
33
|
+
components_result = get_components(file_key: file_key)
|
34
|
+
figma_components = components_result&.meta&.components || []
|
35
|
+
scales = [1.0] if scales.nil? || scales.empty?
|
36
|
+
|
37
|
+
unless name_filter_regex.nil?
|
38
|
+
figma_components = figma_components.select { |c| c.name[Regexp.new(name_filter_regex)] }
|
39
|
+
end
|
40
|
+
|
41
|
+
unless desc_filter_regex.nil?
|
42
|
+
figma_components = figma_components.select { |c| c.description[Regexp.new(desc_filter_regex)] }
|
43
|
+
end
|
44
|
+
|
45
|
+
unless frame_filter_regex.nil?
|
46
|
+
figma_components = figma_components.select { |c| c.containing_frame&.name[Regexp.new(frame_filter_regex)] }
|
47
|
+
end
|
48
|
+
|
49
|
+
unless page_filter_regex.nil?
|
50
|
+
figma_components = figma_components.select { |c| c.containing_frame&.page_name[Regexp.new(page_filter_regex)] }
|
51
|
+
end
|
52
|
+
|
53
|
+
node_ids = figma_components.map(&:node_id)
|
54
|
+
node_id_batches = node_ids.each_slice(request_batch || 1_000_000).to_a
|
55
|
+
image_links_hash = {}
|
56
|
+
|
57
|
+
scales.each do |scale|
|
58
|
+
images_all = {}
|
59
|
+
node_id_batches.each do |batch_node_ids|
|
60
|
+
images = get_images(
|
61
|
+
file_key: file_key,
|
62
|
+
node_ids: batch_node_ids,
|
63
|
+
image_format: image_format,
|
64
|
+
scale: scale,
|
65
|
+
svg_include_id: svg_include_id,
|
66
|
+
svg_simplify_stroke: svg_simplify_stroke,
|
67
|
+
use_absolute_bounds: use_absolute_bounds
|
68
|
+
)&.images || {}
|
69
|
+
images_all.merge!(images)
|
70
|
+
end
|
71
|
+
image_links_hash[scale] = images_all
|
72
|
+
end
|
73
|
+
|
74
|
+
components = converter.fulfill_components(
|
75
|
+
figma_components: figma_components,
|
76
|
+
image_links_hash: image_links_hash,
|
77
|
+
image_format: image_format
|
78
|
+
)
|
79
|
+
|
80
|
+
ComponentsList.new(components: components)
|
81
|
+
end
|
82
|
+
|
83
|
+
def get_components(file_key:)
|
84
|
+
uri = URI.parse("#{@base_url}/files/#{file_key}/components")
|
85
|
+
|
86
|
+
req = create_request(uri)
|
87
|
+
http = create_http_client(uri)
|
88
|
+
|
89
|
+
res = http.start do |h|
|
90
|
+
h.request(req)
|
91
|
+
end
|
92
|
+
|
93
|
+
raise "HTTP Status Code: #{res.code}\n#{res.body}" unless res.is_a?(Net::HTTPSuccess)
|
94
|
+
|
95
|
+
json_data = JSON.parse(res.body)
|
96
|
+
FigmaComponentsStylesResult.from_hash(json_data)
|
97
|
+
end
|
98
|
+
|
99
|
+
def get_images(file_key:, node_ids:, image_format:, scale:, use_absolute_bounds:, svg_include_id:, svg_simplify_stroke:)
|
100
|
+
uri = URI.parse("#{@base_url}/images/#{file_key}")
|
101
|
+
params = {}
|
102
|
+
params['ids'] = node_ids.join(',')
|
103
|
+
params['format'] = image_format unless image_format.nil?
|
104
|
+
params['scale'] = scale unless scale.nil?
|
105
|
+
params['use_absolute_bounds'] = use_absolute_bounds unless use_absolute_bounds.nil?
|
106
|
+
params['svg_include_id'] = svg_include_id unless svg_include_id.nil?
|
107
|
+
params['svg_simplify_stroke'] = svg_simplify_stroke unless svg_simplify_stroke.nil?
|
108
|
+
add_query_parameters(uri: uri, params: params)
|
109
|
+
|
110
|
+
req = create_request(uri)
|
111
|
+
http = create_http_client(uri)
|
112
|
+
|
113
|
+
res = http.start do |h|
|
114
|
+
h.request(req)
|
115
|
+
end
|
116
|
+
|
117
|
+
raise "HTTP Status Code: #{res.code}\n#{res.body}" unless res.is_a?(Net::HTTPSuccess)
|
118
|
+
|
119
|
+
json_data = JSON.parse(res.body)
|
120
|
+
FigmaImagesResult.from_hash(json_data)
|
121
|
+
end
|
122
|
+
|
123
|
+
def get_full_styles(file_key:, request_batch:)
|
124
|
+
converter = FigmaToDomainConverter.new
|
125
|
+
styles_result = get_styles(file_key: file_key)
|
126
|
+
node_ids = (styles_result&.meta&.styles || []).map(&:node_id)
|
127
|
+
node_id_batches = node_ids.each_slice(request_batch || 1_000_000).to_a
|
128
|
+
|
129
|
+
nodes_result = FigmaNodesResult.new(name: nil, nodes: {})
|
130
|
+
|
131
|
+
node_id_batches.each do |batch_node_ids|
|
132
|
+
result = get_nodes(file_key: file_key, node_ids: batch_node_ids, depth: 1)
|
133
|
+
nodes_result.name = result.name
|
134
|
+
nodes_result.nodes.merge!(result.nodes)
|
135
|
+
end
|
136
|
+
|
137
|
+
converter.fulfill_styles(figma_nodes_result: nodes_result, figma_styles_result: styles_result)
|
138
|
+
end
|
139
|
+
|
140
|
+
def get_styles(file_key:)
|
141
|
+
uri = URI.parse("#{@base_url}/files/#{file_key}/styles")
|
142
|
+
|
143
|
+
req = create_request(uri)
|
144
|
+
http = create_http_client(uri)
|
145
|
+
|
146
|
+
res = http.start do |h|
|
147
|
+
h.request(req)
|
148
|
+
end
|
149
|
+
|
150
|
+
raise "HTTP Status Code: #{res.code}\n#{res.body}" unless res.is_a?(Net::HTTPSuccess)
|
151
|
+
|
152
|
+
json_data = JSON.parse(res.body)
|
153
|
+
FigmaComponentsStylesResult.from_hash(json_data)
|
154
|
+
end
|
155
|
+
|
156
|
+
def get_nodes(file_key:, node_ids:, depth: 1)
|
157
|
+
uri = URI.parse("#{@base_url}/files/#{file_key}/nodes")
|
158
|
+
node_ids_string = node_ids.join(',')
|
159
|
+
params = { "ids": node_ids_string, "depth": depth }
|
160
|
+
add_query_parameters(uri: uri, params: params)
|
161
|
+
|
162
|
+
req = create_request(uri)
|
163
|
+
http = create_http_client(uri)
|
164
|
+
|
165
|
+
res = http.start do |h|
|
166
|
+
h.request(req)
|
167
|
+
end
|
168
|
+
|
169
|
+
raise "HTTP Status Code: #{res.code}\n#{res.body}" unless res.is_a?(Net::HTTPSuccess)
|
170
|
+
|
171
|
+
json_data = JSON.parse(res.body)
|
172
|
+
FigmaNodesResult.from_hash(json_data)
|
173
|
+
end
|
174
|
+
|
175
|
+
private
|
176
|
+
|
177
|
+
def add_query_parameters(uri:, params:)
|
178
|
+
uri.query = URI.encode_www_form(URI.decode_www_form(uri.query || '').concat(params.to_a))
|
179
|
+
end
|
180
|
+
|
181
|
+
def create_request(uri)
|
182
|
+
req = Net::HTTP::Get.new(uri.request_uri.to_s)
|
183
|
+
req[CONTENT_TYPE_HEADER_NAME] = CONTENT_TYPE_HEADER_VALUE
|
184
|
+
req[TOKEN_HEADER_NAME] = @access_token
|
185
|
+
req
|
186
|
+
end
|
187
|
+
|
188
|
+
def create_http_client(uri)
|
189
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
190
|
+
http.use_ssl = true
|
191
|
+
http
|
192
|
+
end
|
193
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class FigmaColor
|
4
|
+
attr_accessor :r, :g, :b, :a
|
5
|
+
|
6
|
+
def initialize(r:, g:, b:, a:)
|
7
|
+
@r = r
|
8
|
+
@g = g
|
9
|
+
@b = b
|
10
|
+
@a = a
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.from_hash(hash)
|
14
|
+
return nil if hash.nil?
|
15
|
+
|
16
|
+
r = hash['r']
|
17
|
+
g = hash['g']
|
18
|
+
b = hash['b']
|
19
|
+
a = hash['a']
|
20
|
+
|
21
|
+
return FigmaColor.new(r: r, g: g, b: b, a: a) if !r.nil? && !g.nil? && !b.nil? && !a.nil?
|
22
|
+
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_hash
|
27
|
+
hash = {}
|
28
|
+
hash['r'] = r
|
29
|
+
hash['g'] = g
|
30
|
+
hash['b'] = b
|
31
|
+
hash['a'] = a
|
32
|
+
|
33
|
+
hash
|
34
|
+
end
|
35
|
+
end
|