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
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'figma_frame_info'
4
+
5
+ class FigmaComponent
6
+ attr_accessor \
7
+ :key,
8
+ :file_key,
9
+ :node_id,
10
+ :name,
11
+ :description,
12
+ :containing_frame
13
+
14
+ def initialize(key:, file_key:, node_id:, name:, description:, containing_frame:)
15
+ @key = key
16
+ @file_key = file_key
17
+ @node_id = node_id
18
+ @name = name
19
+ @description = description
20
+ @containing_frame = containing_frame
21
+ end
22
+
23
+ def self.from_hash(hash)
24
+ return nil if hash.nil?
25
+
26
+ key = hash['key']
27
+ file_key = hash['file_key']
28
+ node_id = hash['node_id']
29
+ name = hash['name']
30
+ description = hash['description']
31
+ containing_frame_hash = hash['containing_frame']
32
+ containing_frame = containing_frame_hash.nil? ? nil : FigmaFrameInfo.from_hash(containing_frame_hash)
33
+
34
+ if !key.nil? && !file_key.nil? && !node_id.nil? && !name.nil?
35
+ return FigmaComponent.new(
36
+ key: key,
37
+ file_key: file_key,
38
+ node_id: node_id,
39
+ name: name,
40
+ description: description,
41
+ containing_frame: containing_frame
42
+ )
43
+ end
44
+
45
+ nil
46
+ end
47
+
48
+ def to_hash
49
+ hash = {}
50
+ hash['key'] = key
51
+ hash['file_key'] = file_key
52
+ hash['node_id'] = node_id
53
+ hash['name'] = name
54
+ hash['description'] = description
55
+ hash['containing_frame'] = containing_frame.to
56
+ hash
57
+ end
58
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'figma_style'
4
+ require_relative 'figma_component'
5
+
6
+ class FigmaComponentsStylesMeta
7
+ attr_accessor :styles, :components
8
+
9
+ def initialize(styles:, components:)
10
+ @styles = styles
11
+ @components = components
12
+ end
13
+
14
+ def self.from_hash(hash)
15
+ styles_hash = hash['styles']
16
+ components_hash = hash['components']
17
+
18
+ styles = styles_hash.nil? ? nil : styles_hash.map { |s| FigmaStyle.from_hash(s) }
19
+ components = components_hash.nil? ? nil : components_hash.map { |c| FigmaComponent.from_hash(c) }
20
+
21
+ FigmaComponentsStylesMeta.new(styles: styles, components: components)
22
+ end
23
+
24
+ def to_hash
25
+ hash = {}
26
+ hash['styles'] = styles.map(&:to_hash) unless styles.nil?
27
+ hash['components'] = components.map(&:to_hash) unless components.nil?
28
+ hash
29
+ end
30
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FigmaFrameInfo
4
+ attr_accessor \
5
+ :node_id,
6
+ :name,
7
+ :background_color,
8
+ :page_id,
9
+ :page_name
10
+
11
+ def initialize(node_id:, name:, background_color:, page_id:, page_name:)
12
+ @node_id = node_id
13
+ @name = name
14
+ @background_color = background_color
15
+ @page_id = page_id
16
+ @page_name = page_name
17
+ end
18
+
19
+ def self.from_hash(hash)
20
+ return nil if hash.nil?
21
+
22
+ node_id = hash['nodeId']
23
+ name = hash['name']
24
+ background_color = hash['backgroundColor']
25
+ page_id = hash['pageId']
26
+ page_name = hash['pageName']
27
+
28
+ FigmaFrameInfo.new(
29
+ node_id: node_id,
30
+ name: name,
31
+ background_color: background_color,
32
+ page_id: page_id,
33
+ page_name: page_name
34
+ )
35
+ end
36
+
37
+ def to_hash
38
+ hash = {}
39
+ hash['nodeId'] = node_id
40
+ hash['name'] = name
41
+ hash['backgroundColor'] = background_color
42
+ hash['pageId'] = page_id
43
+ hash['pageName'] = page_name
44
+ hash
45
+ end
46
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'figma_paint'
4
+ require_relative 'figma_type_style'
5
+
6
+ class FigmaNode
7
+ attr_accessor :id, :name, :type, :style, :fills
8
+
9
+ def initialize(id:, name:, type:, style:, fills:)
10
+ @id = id
11
+ @name = name
12
+ @type = type
13
+ @style = style
14
+ @fills = fills
15
+ end
16
+
17
+ def self.from_hash(hash)
18
+ return nil if hash.nil?
19
+
20
+ id = hash['id']
21
+ name = hash['name']
22
+ type = hash['type']
23
+ style_hash = hash['style']
24
+ style = style_hash.nil? ? nil : FigmaTypeStyle.from_hash(style_hash)
25
+ fills = (hash['fills'] || []).map { |f| FigmaPaint.from_hash(f) }.compact || []
26
+
27
+ if !id.nil? && !name.nil? && !type.nil?
28
+ return FigmaNode.new(id: id, name: name, type: type, style: style, fills: fills)
29
+ end
30
+
31
+ nil
32
+ end
33
+
34
+ def to_hash
35
+ hash = {}
36
+ hash['id'] = id
37
+ hash['name'] = name
38
+ hash['type'] = type
39
+ hash['style'] = style.to_hash unless style.nil?
40
+ hash['fills'] = (fills || []).map(&:to_hash)
41
+ hash
42
+ end
43
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'figma_color'
4
+
5
+ class FigmaPaint
6
+ attr_accessor :type, :color, :opacity
7
+
8
+ def initialize(type:, color:, opacity:)
9
+ @type = type
10
+ @color = color
11
+ @opacity = opacity
12
+ end
13
+
14
+ def self.from_hash(hash)
15
+ return nil if hash.nil?
16
+
17
+ type = hash['type']
18
+ color = FigmaColor.from_hash(hash['color'])
19
+ opacity = hash['opacity'] || 1.0
20
+
21
+ return FigmaPaint.new(type: type, color: color, opacity: opacity) if !type.nil? && !color.nil?
22
+
23
+ nil
24
+ end
25
+
26
+ def to_hash
27
+ hash = {}
28
+ hash['type'] = type
29
+ hash['color'] = color.to_hash unless color.nil?
30
+ hash
31
+ end
32
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ FIGMA_STYLE_TYPE_FILL = 'FILL'
4
+ FIGMA_STYLE_TYPE_TEXT = 'TEXT'
5
+
6
+ class FigmaStyle
7
+ attr_accessor \
8
+ :name,
9
+ :node_id,
10
+ :style_type,
11
+ :description
12
+
13
+ def initialize(
14
+ name:,
15
+ node_id:,
16
+ style_type:,
17
+ description:
18
+ )
19
+ @name = name
20
+ @node_id = node_id
21
+ @style_type = style_type
22
+ @description = description
23
+ end
24
+
25
+ def self.from_hash(hash)
26
+ return nil if hash.nil?
27
+
28
+ name = hash['name']
29
+ node_id = hash['node_id']
30
+ style_type = hash['style_type']
31
+ description = hash['description']
32
+
33
+ if !name.nil? && !node_id.nil? && style_type
34
+ return FigmaStyle.new(name: name, node_id: node_id, style_type: style_type, description: description)
35
+ end
36
+
37
+ nil
38
+ end
39
+
40
+ def to_hash
41
+ hash = {}
42
+ hash['name'] = name
43
+ hash['node_id'] = node_id
44
+ hash['style_type'] = style_type
45
+ hash['description'] = description
46
+ hash
47
+ end
48
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FigmaTypeStyle
4
+ attr_accessor \
5
+ :font_family,
6
+ :font_postscript_name,
7
+ :font_size,
8
+ :font_weight,
9
+ :letter_spacing
10
+
11
+ def initialize(
12
+ font_family:,
13
+ font_postscript_name:,
14
+ font_size:,
15
+ font_weight:,
16
+ letter_spacing:
17
+ )
18
+ @font_family = font_family
19
+ @font_postscript_name = font_postscript_name
20
+ @font_size = font_size
21
+ @font_weight = font_weight
22
+ @letter_spacing = letter_spacing
23
+ end
24
+
25
+ def self.from_hash(hash)
26
+ return nil if hash.nil?
27
+
28
+ font_family = hash['fontFamily']
29
+ font_postscript_name = hash['fontPostScriptName']
30
+ font_size = hash['fontSize']
31
+ font_weight = hash['fontWeight']
32
+ letter_spacing = hash['letterSpacing']
33
+
34
+ if !font_family.nil? && !font_size.nil? && !font_weight.nil? && !letter_spacing.nil?
35
+ return FigmaTypeStyle.new(
36
+ font_family: font_family,
37
+ font_postscript_name: font_postscript_name,
38
+ font_size: font_size,
39
+ font_weight: font_weight,
40
+ letter_spacing: letter_spacing
41
+ )
42
+ end
43
+
44
+ nil
45
+ end
46
+
47
+ def to_hash
48
+ hash = {}
49
+ hash['fontFamily'] = font_family
50
+ hash['fontPostscriptName'] = font_postscript_name
51
+ hash['fontSize'] = font_size
52
+ hash['fontWeight'] = font_weight
53
+ hash['letterSpacing'] = letter_spacing
54
+ hash
55
+ end
56
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../primitives/figma_components_styles_meta'
4
+
5
+ class FigmaComponentsStylesResult
6
+ attr_accessor :error, :status, :meta
7
+
8
+ def initialize(error:, status:, meta:)
9
+ @error = error
10
+ @status = status
11
+ @meta = meta
12
+ end
13
+
14
+ def self.from_hash(hash)
15
+ return nil if hash.nil?
16
+
17
+ error = hash['error']
18
+ status = hash['status']
19
+ meta_hash = hash['meta']
20
+ meta = meta_hash.nil? ? nil : FigmaComponentsStylesMeta.from_hash(meta_hash)
21
+
22
+ if !error.nil? && !status.nil? && !meta.nil?
23
+ return FigmaComponentsStylesResult.new(error: error, status: status, meta: meta)
24
+ end
25
+
26
+ nil
27
+ end
28
+
29
+ def to_hash
30
+ hash = {}
31
+ hash['error'] = error
32
+ hash['status'] = status
33
+ hash['meta'] = meta.to_hash unless meta.nil?
34
+ hash
35
+ end
36
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FigmaImagesResult
4
+ attr_accessor :error, :images
5
+
6
+ def initialize(error:, images:)
7
+ @error = error
8
+ @images = images || []
9
+ end
10
+
11
+ def self.from_hash(hash)
12
+ return nil if hash.nil?
13
+
14
+ error = hash['err']
15
+ images = hash['images']
16
+ FigmaImagesResult.new(error: error, images: images)
17
+ end
18
+
19
+ def to_hash
20
+ hash = {}
21
+ hash['err'] = error
22
+ hash['images'] = images
23
+ hash
24
+ end
25
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../primitives/figma_node'
4
+
5
+ class FigmaNodesResult
6
+ attr_accessor :name, :nodes
7
+
8
+ def initialize(name:, nodes:)
9
+ @name = name
10
+ @nodes = nodes
11
+ end
12
+
13
+ def self.from_hash(hash)
14
+ return nil if hash.nil?
15
+
16
+ name = hash['name']
17
+ nodes = FigmaNodesResult.parse_nodes(hash: hash['nodes'])
18
+
19
+ return FigmaNodesResult.new(name: name, nodes: nodes) if !name.nil? && !nodes.nil?
20
+
21
+ nil
22
+ end
23
+
24
+ def to_hash
25
+ hash = {}
26
+ hash['name'] = name
27
+ nodes_hash = (nodes || {})
28
+ nodes_result_hash = {}
29
+
30
+ nodes_hash.each do |k, v|
31
+ node = v['document'].to_hash
32
+ nodes_result_hash[k] = { 'document' => node } unless node.nil?
33
+ end
34
+
35
+ hash['nodes'] = nodes_result_hash
36
+ hash
37
+ end
38
+
39
+ def self.parse_nodes(hash:)
40
+ return nil if hash.nil?
41
+
42
+ result_hash = {}
43
+
44
+ hash.each do |k, v|
45
+ node_hash = v['document']
46
+ node = FigmaNode.from_hash(node_hash)
47
+ result_hash[k] = { 'document' => node } unless node.nil?
48
+ end
49
+
50
+ result_hash
51
+ end
52
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../models'
4
+
5
+ class FigmaToDomainConverter
6
+ def convert_figma_style(figma_style:)
7
+ return nil if figma_style.nil?
8
+
9
+ Style.new(
10
+ name: figma_style.name,
11
+ node_id: figma_style.node_id,
12
+ style_type: figma_style.style_type,
13
+ description: figma_style.description
14
+ )
15
+ end
16
+
17
+ def fulfill_styles(figma_nodes_result:, figma_styles_result:)
18
+ return [] if figma_nodes_result.nil? || figma_styles_result.nil?
19
+
20
+ styles = []
21
+ figma_styles = figma_styles_result&.meta&.styles || []
22
+ nodes = figma_nodes_result&.nodes || {}
23
+
24
+ figma_styles.each do |s|
25
+ style = convert_figma_style(figma_style: s)
26
+ node = nodes.dig(s.node_id, 'document')
27
+
28
+ case s.style_type
29
+ when FIGMA_STYLE_TYPE_FILL
30
+ fills = node&.fills
31
+ next if fills.nil?
32
+
33
+ figma_paint = fills[0]
34
+ next if figma_paint.nil?
35
+
36
+ style&.color = Color.new(
37
+ r: figma_paint.color.r,
38
+ g: figma_paint.color.g,
39
+ b: figma_paint.color.b,
40
+ a: figma_paint.opacity
41
+ )
42
+ styles.push(style)
43
+ when FIGMA_STYLE_TYPE_TEXT
44
+ type_style = node&.style
45
+ next if type_style.nil?
46
+
47
+ style&.font = Font.new(
48
+ font_family: type_style.font_family,
49
+ font_postscript_name: type_style.font_postscript_name,
50
+ font_size: type_style.font_size,
51
+ font_weight: type_style.font_weight,
52
+ letter_spacing: type_style.letter_spacing
53
+ )
54
+ styles.push(style)
55
+ else
56
+ next
57
+ end
58
+ end
59
+
60
+ styles
61
+ end
62
+
63
+ def fulfill_components(figma_components:, image_links_hash:, image_format:)
64
+ return [] if figma_components.nil? || image_links_hash.nil?
65
+
66
+ components = []
67
+ figma_components.each do |c|
68
+ component = Component.new(
69
+ key: c.key,
70
+ file_key: c.file_key,
71
+ node_id: c.node_id,
72
+ name: c.name,
73
+ description: c.description
74
+ )
75
+
76
+ component.containing_frame = FrameInfo.new(
77
+ node_id: c.containing_frame.node_id,
78
+ name: c.containing_frame.name,
79
+ background_color: c.containing_frame.background_color,
80
+ page_id: c.containing_frame.page_id,
81
+ page_name: c.containing_frame.page_name
82
+ )
83
+
84
+ image_links = []
85
+ image_links_hash.each do |key, link_hash|
86
+ link = link_hash[c.node_id]
87
+ if !link.nil? && !key.nil? && !image_format.nil?
88
+ image_link = ImageLink.new(link: link, scale: key, image_format: image_format)
89
+ image_links.push(image_link)
90
+ end
91
+ end
92
+ component.image_links = image_links
93
+ components.push(component)
94
+ end
95
+
96
+ components
97
+ end
98
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Color
4
+ attr_accessor :r, :g, :b, :a
5
+
6
+ def hex
7
+ format('#%02x%02x%02x', *[r, g, b].map { |v| v.to_f * 255 }).upcase
8
+ end
9
+
10
+ def hex_with_alpha
11
+ format('#%02x%02x%02x%02x', *[a, r, g, b].map { |v| v.to_f * 255 }).upcase
12
+ end
13
+
14
+ def initialize(r:, g:, b:, a:)
15
+ @r = r
16
+ @g = g
17
+ @b = b
18
+ @a = a
19
+ end
20
+
21
+ def self.from_hash(hash)
22
+ return nil if hash.nil?
23
+
24
+ r = hash['r']
25
+ g = hash['g']
26
+ b = hash['b']
27
+ a = hash['a']
28
+
29
+ return Color.new(r: r, g: g, b: b, a: a) if !r.nil? && !g.nil? && !b.nil? && !a.nil?
30
+
31
+ nil
32
+ end
33
+
34
+ def to_hash
35
+ hash = {}
36
+ hash['r'] = r
37
+ hash['g'] = g
38
+ hash['b'] = b
39
+ hash['a'] = a
40
+ hash['hex'] = hex
41
+ hash['hex_with_alpha'] = hex_with_alpha
42
+ hash
43
+ end
44
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'image_link'
4
+ require_relative 'frame_info'
5
+
6
+ class Component
7
+ attr_accessor \
8
+ :key,
9
+ :file_key,
10
+ :node_id,
11
+ :name,
12
+ :description,
13
+ :containing_frame,
14
+ :image_links
15
+
16
+ def single_scale?
17
+ links = image_links || []
18
+ return false if links.length > 1
19
+
20
+ link = links[0]
21
+ return false if link.nil?
22
+
23
+ image_format = link&.image_format
24
+ return false if image_format.nil?
25
+
26
+ scale = link.scale
27
+ image_format = image_format.downcase
28
+ scale == 1.0 && %w[svg pdf].include?(image_format)
29
+ end
30
+
31
+ def description_params
32
+ JSON.parse(description)
33
+ rescue StandardError
34
+ nil
35
+ end
36
+
37
+ def initialize(key:, file_key:, node_id:, name:, description:, containing_frame: nil, image_links: nil)
38
+ @key = key
39
+ @file_key = file_key
40
+ @node_id = node_id
41
+ @name = name
42
+ @description = description
43
+ @containing_frame = containing_frame
44
+ @image_links = image_links
45
+ end
46
+
47
+ def self.from_hash(hash)
48
+ return nil if hash.nil?
49
+
50
+ key = hash['key']
51
+ file_key = hash['file_key']
52
+ node_id = hash['node_id']
53
+ name = hash['name']
54
+ description = hash['description']
55
+ images_array = hash['image_links']
56
+ image_links = images_array&.map { |i| ImageLink.from_hash(i) }
57
+ containing_frame = FrameInfo.from_hash(hash['containing_frame'])
58
+
59
+ if !key.nil? && !file_key.nil? && !node_id.nil? && !name.nil?
60
+ return Component.new(
61
+ key: key,
62
+ file_key: file_key,
63
+ node_id: node_id,
64
+ name: name,
65
+ description: description,
66
+ containing_frame: containing_frame,
67
+ image_links: image_links
68
+ )
69
+ end
70
+
71
+ nil
72
+ end
73
+
74
+ def to_hash
75
+ hash = {}
76
+ hash['key'] = key
77
+ hash['file_key'] = file_key
78
+ hash['node_id'] = node_id
79
+ hash['name'] = name
80
+ hash['description'] = description
81
+ hash['image_links'] = image_links&.map { |i| i.to_hash } unless image_links.nil?
82
+ hash['description_params'] = description_params unless description_params.nil?
83
+ hash['containing_frame'] = containing_frame&.to_hash
84
+ hash
85
+ end
86
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require_relative 'component'
5
+
6
+ class ComponentsList
7
+ attr_accessor :components
8
+
9
+ def initialize(components:)
10
+ @components = components
11
+ end
12
+
13
+ def self.from_hash(hash)
14
+ hash.map { |s| Component.from_hash(s) }
15
+ end
16
+
17
+ def to_hash
18
+ components.map(&:to_hash)
19
+ end
20
+
21
+ def self.from_json(json)
22
+ components = (JSON.parse(json) || []).map { |s| Component.from_hash(s) }
23
+ ComponentsList.new(components: components)
24
+ end
25
+
26
+ def to_json(*_args)
27
+ components.map(&:to_hash).to_json
28
+ end
29
+ end