admiral-tools-figma 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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