admiral-tools-figma 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +1 -0
  3. data/admiral-tools-figma.gemspec +9 -0
  4. data/admiraltools/actions/figma_download_images.rb +116 -0
  5. data/admiraltools/actions/figma_generate_styles.rb +77 -0
  6. data/admiraltools/actions/figma_get_image_links.rb +139 -0
  7. data/admiraltools/actions/figma_get_styles.rb +90 -0
  8. data/admiraltools/helper/admiraltools_helper.rb +18 -0
  9. data/admiraltools/helper/figma/figma_client/figma_client.rb +193 -0
  10. data/admiraltools/helper/figma/figma_client/models/api/primitives/figma_color.rb +35 -0
  11. data/admiraltools/helper/figma/figma_client/models/api/primitives/figma_component.rb +58 -0
  12. data/admiraltools/helper/figma/figma_client/models/api/primitives/figma_components_styles_meta.rb +30 -0
  13. data/admiraltools/helper/figma/figma_client/models/api/primitives/figma_frame_info.rb +46 -0
  14. data/admiraltools/helper/figma/figma_client/models/api/primitives/figma_node.rb +43 -0
  15. data/admiraltools/helper/figma/figma_client/models/api/primitives/figma_paint.rb +32 -0
  16. data/admiraltools/helper/figma/figma_client/models/api/primitives/figma_style.rb +48 -0
  17. data/admiraltools/helper/figma/figma_client/models/api/primitives/figma_type_style.rb +56 -0
  18. data/admiraltools/helper/figma/figma_client/models/api/results/figma_components_styles_result.rb +36 -0
  19. data/admiraltools/helper/figma/figma_client/models/api/results/figma_images_result.rb +25 -0
  20. data/admiraltools/helper/figma/figma_client/models/api/results/figma_nodes_result.rb +52 -0
  21. data/admiraltools/helper/figma/figma_client/models/domain/converters/figma_to_domain_converter.rb +98 -0
  22. data/admiraltools/helper/figma/figma_client/models/domain/primitives/color.rb +44 -0
  23. data/admiraltools/helper/figma/figma_client/models/domain/primitives/component.rb +86 -0
  24. data/admiraltools/helper/figma/figma_client/models/domain/primitives/components_list.rb +29 -0
  25. data/admiraltools/helper/figma/figma_client/models/domain/primitives/font.rb +56 -0
  26. data/admiraltools/helper/figma/figma_client/models/domain/primitives/frame_info.rb +46 -0
  27. data/admiraltools/helper/figma/figma_client/models/domain/primitives/image_link.rb +33 -0
  28. data/admiraltools/helper/figma/figma_client/models/domain/primitives/style.rb +113 -0
  29. data/admiraltools/helper/figma/figma_client/models/domain/primitives/styles_group.rb +22 -0
  30. data/admiraltools/helper/figma/figma_client/models/domain/primitives/styles_list.rb +29 -0
  31. data/admiraltools/helper/figma/figma_client/models/models.rb +25 -0
  32. data/admiraltools/helper/figma/figma_image_downloader/bin/svg2vectordrawable.jar +0 -0
  33. data/admiraltools/helper/figma/figma_image_downloader/figma_image_downloader.rb +65 -0
  34. data/admiraltools/helper/figma/figma_image_downloader/generators/folder_generator.rb +47 -0
  35. data/admiraltools/helper/figma/figma_image_downloader/generators/folder_generator_raw.rb +32 -0
  36. data/admiraltools/helper/figma/figma_image_downloader/generators/folder_generator_res.rb +38 -0
  37. data/admiraltools/helper/figma/figma_image_downloader/generators/folder_generator_xcassets.rb +180 -0
  38. data/admiraltools/helper/figma/figma_image_downloader/generators/generators.rb +6 -0
  39. data/admiraltools/helper/figma/figma_image_downloader/helpers/image_name_formatter.rb +72 -0
  40. data/admiraltools/helper/figma/figma_image_downloader/helpers/progress_tasks_logger.rb +22 -0
  41. data/admiraltools/helper/figma/figma_image_downloader/helpers/scale_formatter.rb +96 -0
  42. data/admiraltools/helper/figma/figma_image_downloader/helpers/svg_to_vector_converter.rb +12 -0
  43. data/admiraltools/helper/figma/figma_image_downloader/models/download_image_params.rb +31 -0
  44. data/admiraltools/helper/figma/figma_image_downloader/models/image_name_components.rb +10 -0
  45. data/admiraltools/helper/figma/figma_image_downloader/models/image_ref.rb +12 -0
  46. data/admiraltools/helper/figma/figma_image_downloader/models/image_set_ref.rb +14 -0
  47. data/admiraltools/helper/figma/figma_image_downloader/models/models.rb +5 -0
  48. data/admiraltools/helper/figma/figma_template_generator/figma_template_generator.rb +22 -0
  49. data/admiraltools/helper/figma/figma_template_generator/models/styles_template.rb +34 -0
  50. data/admiraltools/helper/figma/helpers/file_folder.rb +30 -0
  51. data/admiraltools/helper/figma/helpers/hash_access.rb +18 -0
  52. data/admiraltools/helper/figma/helpers/string_filters.rb +63 -0
  53. data/admiraltools/helper/figma/helpers/string_format.rb +60 -0
  54. data/admiraltools/helper/system/system.rb +17 -0
  55. data/admiraltools/version.rb +7 -0
  56. data/admiraltools.rb +18 -0
  57. 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