line-message-builder 0.7.0 → 0.8.0

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.
@@ -3,27 +3,139 @@
3
3
  module Line
4
4
  module Message
5
5
  module Builder
6
- # The context is wrapper for the context object to provide assigns support.
6
+ # The `Context` class is a crucial component of the `Line::Message::Builder`
7
+ # DSL, enabling a flexible and dynamic environment for message construction.
8
+ # It acts as a wrapper around an optional user-provided context object and
9
+ # manages a separate hash of "assigns" (local variables for the DSL).
10
+ #
11
+ # The primary purposes of the `Context` are:
12
+ # 1. **To provide access to helper methods**: If a user passes a context object
13
+ # (e.g., a Rails view context, a presenter, or any custom object) during
14
+ # builder initialization (`Line::Message::Builder.with(my_helper_object)`),
15
+ # methods defined on `my_helper_object` become directly callable within
16
+ # the DSL blocks.
17
+ # 2. **To allow local data storage (`assigns`)**: The `assigns` hash allows
18
+ # for setting and retrieving temporary data that can be shared across
19
+ # different parts of a complex message construction block, without needing
20
+ # to pass it explicitly or pollute the user-provided context.
21
+ #
22
+ # Method calls within the DSL that are not defined on the builder objects
23
+ # themselves are resolved by `Context` in the following order:
24
+ # - First, it checks if the method name corresponds to a key in the `assigns` hash.
25
+ # - If not found in `assigns`, it checks if the wrapped user-context object
26
+ # responds to the method.
27
+ # - If neither, the call proceeds up the normal Ruby method lookup chain.
28
+ #
29
+ # This mechanism allows for a clean and powerful way to integrate external logic
30
+ # and data into the message building process.
31
+ #
32
+ # @example Using a custom context
33
+ # class MyHelpers
34
+ # def current_user_name
35
+ # "Alice"
36
+ # end
37
+ # end
38
+ #
39
+ # helpers = MyHelpers.new
40
+ # Line::Message::Builder.with(helpers) do |root|
41
+ # # `current_user_name` is resolved from `helpers` by Context
42
+ # root.text "Hello, #{current_user_name}!"
43
+ #
44
+ # # Using assigns
45
+ # assigns[:item_count] = 5
46
+ # root.text "You have #{assigns[:item_count]} items."
47
+ # end
7
48
  class Context
49
+ # @!attribute assigns
50
+ # A hash for storing arbitrary data that can be accessed within the
51
+ # builder DSL. This is useful for temporary variables or shared state
52
+ # during message construction.
53
+ # @return [Hash] The hash of assigned values.
54
+ # @example
55
+ # context.assigns[:user_id] = 123
56
+ # puts context.assigns[:user_id] # => 123
8
57
  attr_accessor :assigns
9
58
 
10
- def initialize(context)
59
+ # @!attribute [r] mode
60
+ # The mode in which the builder is operating. This affects how messages
61
+ # are formatted in the final output.
62
+ # @return [Symbol] Either `:api` for direct LINE Messaging API format
63
+ # or `:sdkv2` for LINE Bot SDK v2 compatible format.
64
+ attr_reader :mode
65
+
66
+ # Initializes a new Context object.
67
+ #
68
+ # @param context [Object, nil] An optional object whose methods will be made
69
+ # available within the DSL. If `nil`, only `assigns` and standard
70
+ # builder methods will be available.
71
+ # @param mode [Symbol] The mode of the context, which can be `:api` (default)
72
+ # for direct LINE Messaging API format or `:sdkv2` for LINE Bot SDK v2
73
+ # compatible format.
74
+ def initialize(context, mode: :api)
11
75
  @context = context
12
76
  @assigns = {}
77
+ @mode = mode
13
78
  end
14
79
 
80
+ # Part of Ruby's dynamic method dispatch. It's overridden here to declare
81
+ # that instances of `Context` can respond to methods that are either:
82
+ # 1. Keys in the `@assigns` hash.
83
+ # 2. Methods to which the wrapped `@context` object responds.
84
+ #
85
+ # This ensures that `respond_to?` behaves consistently with how
86
+ # `method_missing` resolves method calls.
87
+ #
88
+ # @param method_name [Symbol] The name of the method being queried.
89
+ # @param include_private [Boolean] Whether to include private methods in
90
+ # the check.
91
+ # @return [Boolean] `true` if the context can handle the method,
92
+ # `false` otherwise.
93
+ # @!visibility private
15
94
  def respond_to_missing?(method_name, include_private = false)
16
95
  @assigns.key?(method_name) ||
17
- @context.respond_to?(method_name, include_private) ||
96
+ @context.respond_to?(method_name, include_private) || # Check @context directly
18
97
  super
19
98
  end
20
99
 
100
+ # Handles calls to methods not explicitly defined on the `Context` class.
101
+ # The resolution order is:
102
+ # 1. If `method_name` is a key in the `@assigns` hash, its value is returned.
103
+ # 2. If the wrapped `@context` object responds to `method_name`, the call
104
+ # is delegated to `@context`.
105
+ # 3. Otherwise, `super` is called, allowing the standard Ruby method
106
+ # lookup to continue (which will likely result in a `NoMethodError`
107
+ # if the method is truly undefined).
108
+ #
109
+ # This is the core mechanism that allows DSL blocks to seamlessly access
110
+ # data from `assigns` or methods from the user-provided context.
111
+ #
112
+ # @param method_name [Symbol] The name of the invoked method.
113
+ # @param ... [Object] Arguments passed to the method.
114
+ # @return [Object, nil] The value from `@assigns`, the result of the
115
+ # delegated call to `@context`, or raises `NoMethodError` via `super`.
116
+ # @raise [NoMethodError] If the method is not found in `assigns` or
117
+ # on the wrapped context.
118
+ # @!visibility private
21
119
  def method_missing(method_name, ...)
22
120
  return @assigns[method_name] if @assigns.key?(method_name)
121
+ # Check @context directly
23
122
  return @context.public_send(method_name, ...) if @context.respond_to?(method_name)
24
123
 
25
124
  super
26
125
  end
126
+
127
+ # Checks if the current mode is set to SDK v2 compatibility.
128
+ #
129
+ # @return [Boolean] `true` if the mode is `:sdkv2`, `false` otherwise.
130
+ # @example
131
+ # if context.sdkv2?
132
+ # # Format message for LINE Bot SDK v2
133
+ # else
134
+ # # Format message for direct API use
135
+ # end
136
+ def sdkv2?
137
+ mode == :sdkv2
138
+ end
27
139
  end
28
140
  end
29
141
  end
@@ -4,16 +4,61 @@ module Line
4
4
  module Message
5
5
  module Builder
6
6
  module Flex
7
- # The action DSL for flex components.
7
+ # The `Actionable` module provides a DSL for defining an action that can be
8
+ # triggered when a user interacts with certain Flex Message components
9
+ # (e.g., a {Button} component, or an entire {Bubble} or {Box} component
10
+ # if it's made tappable).
11
+ #
12
+ # When a component includes this module, it gains methods like {#message}
13
+ # and {#postback} to associate a specific LINE action with itself. The
14
+ # chosen action is stored in the `action` attribute.
15
+ #
16
+ # @!attribute [r] action
17
+ # @return [Actions::Message, Actions::Postback, nil] The action object
18
+ # associated with this component. `nil` if no action is defined.
19
+ #
20
+ # @see Line::Message::Builder::Actions::Message
21
+ # @see Line::Message::Builder::Actions::Postback
22
+ # @see https://developers.line.biz/en/reference/messaging-api/#action-objects
8
23
  module Actionable
24
+ # @!visibility private
25
+ # Automatically adds an `attr_reader :action` to the class that includes
26
+ # this module.
27
+ # @param base [Class] The class including this module.
9
28
  def self.included(base)
10
29
  base.attr_reader :action
11
30
  end
12
31
 
32
+ # Defines a message action for the component.
33
+ # When the component is tapped, a message with the given `text` is sent
34
+ # from the user to the chat.
35
+ #
36
+ # @param text [String] The text of the message to send.
37
+ # @param options [Hash] Additional options for the message action,
38
+ # such as `:label`. See {Actions::Message#initialize}.
39
+ # @param block [Proc, nil] An optional block, though not typically used
40
+ # directly for message actions here.
41
+ # @return [Actions::Message] The created message action object.
42
+ #
43
+ # @example Setting a message action on a button
44
+ # button_component.message "Hello User!", label: "Send Greeting"
13
45
  def message(text, **options, &)
14
46
  @action = Actions::Message.new(text, context: context, **options, &)
15
47
  end
16
48
 
49
+ # Defines a postback action for the component.
50
+ # When the component is tapped, a postback event with the given `data`
51
+ # is sent to the bot's webhook.
52
+ #
53
+ # @param data [String] The data payload for the postback event.
54
+ # @param options [Hash] Additional options for the postback action,
55
+ # such as `:label` or `:display_text`. See {Actions::Postback#initialize}.
56
+ # @param block [Proc, nil] An optional block, though not typically used
57
+ # directly for postback actions here.
58
+ # @return [Actions::Postback] The created postback action object.
59
+ #
60
+ # @example Setting a postback action on a button
61
+ # button_component.postback "action=buy&item_id=123", label: "Buy Item"
17
62
  def postback(data, **options, &)
18
63
  @action = Actions::Postback.new(data, context: context, **options, &)
19
64
  end
@@ -4,66 +4,182 @@ module Line
4
4
  module Message
5
5
  module Builder
6
6
  module Flex
7
- # The box is a component for the Flex message.
7
+ # Represents a "box" component in a LINE Flex Message.
8
+ #
9
+ # A box is a fundamental layout container that arranges its child components
10
+ # (`contents`) in a specified direction (`layout`: horizontal, vertical, or
11
+ # baseline). It can hold various other components like text, images, buttons,
12
+ # or even nested boxes, allowing for complex layouts.
13
+ #
14
+ # Boxes have numerous properties to control their appearance and the
15
+ # arrangement of their children, such as padding, margin, spacing between
16
+ # items, justification, and alignment.
17
+ #
18
+ # A box can also have an `action` associated with it, making the entire
19
+ # box area tappable.
20
+ #
21
+ # @example Creating a horizontal box with two text components
22
+ # Line::Message::Builder.with do |root|
23
+ # root.flex alt_text: "Box example" do |flex|
24
+ # flex.bubble do |bubble|
25
+ # bubble.body do |body_box| # body_box is an instance of Flex::Box
26
+ # body_box.layout :horizontal
27
+ # body_box.spacing :md
28
+ # body_box.text "Item 1"
29
+ # body_box.text "Item 2"
30
+ # end
31
+ # end
32
+ # end
33
+ # end
34
+ #
35
+ # @see https://developers.line.biz/en/reference/messaging-api/#box
36
+ # @see Actionable For making the box tappable.
37
+ # @see HasPartial For including reusable component groups.
38
+ # @see Position::Padding For padding properties.
39
+ # @see Position::Margin For margin properties.
40
+ # @see Position::Offset For offset properties.
41
+ # @see Size::Flex For flex sizing property.
8
42
  class Box < Line::Message::Builder::Base
9
- include HasPartial
10
- include Actionable
11
- include Position::Padding
12
- include Position::Margin
13
- include Position::Offset
14
- include Size::Flex
43
+ include HasPartial # Allows including predefined partial component sets.
44
+ include Actionable # Enables defining an action for the entire box.
45
+ include Position::Padding # Adds padding options like `padding_all`, `padding_top`, etc.
46
+ include Position::Margin # Adds `margin` option.
47
+ include Position::Offset # Adds offset options like `offset_top`, `offset_start`, etc.
48
+ include Size::Flex # Adds `flex` option for controlling size within a parent box.
15
49
 
50
+ # @!attribute [r] contents
51
+ # @return [Array<Base>] An array holding the child components (e.g.,
52
+ # {Text}, {Image}, nested {Box} instances) of this box.
16
53
  attr_reader :contents
17
54
 
55
+ # Specifies the arrangement of child components.
56
+ # @!method layout(value)
57
+ # @param value [Symbol] `:horizontal` (default), `:vertical`, or `:baseline`.
58
+ # @return [Symbol] The current layout.
18
59
  option :layout, default: :horizontal, validator: Validators::Enum.new(
19
60
  :horizontal, :vertical, :baseline
20
61
  )
62
+
63
+ # Horizontal alignment of content in a horizontal box; vertical alignment in a vertical box.
64
+ # @!method justify_content(value)
65
+ # @param value [Symbol, nil] E.g., `:flex_start`, `:center`, `:flex_end`,
66
+ # `:space_between`, `:space_around`, `:space_evenly`.
67
+ # @return [Symbol, nil] The current justify_content value.
21
68
  option :justify_content, default: nil, validator: Validators::Enum.new(
22
69
  :flex_start, :center, :flex_end, :space_between, :space_around, :space_evenly
23
70
  )
71
+
72
+ # Vertical alignment of content in a horizontal box; horizontal alignment in a vertical box.
73
+ # @!method align_items(value)
74
+ # @param value [Symbol, nil] E.g., `:flex_start`, `:center`, `:flex_end`.
75
+ # @return [Symbol, nil] The current align_items value.
24
76
  option :align_items, default: nil, validator: Validators::Enum.new(
25
77
  :flex_start, :center, :flex_end
26
78
  )
79
+
80
+ # Specifies the minimum spacing between components.
81
+ # Not applicable if `layout` is `:baseline` or if the box contains only one component.
82
+ # @!method spacing(value)
83
+ # @param value [String, Symbol, nil] E.g., `"md"`, `:lg`, `"10px"`.
84
+ # Keywords: `:none`, `:xs`, `:sm`, `:md`, `:lg`, `:xl`, `:xxl`.
85
+ # @return [String, Symbol, nil] The current spacing value.
27
86
  option :spacing, default: nil, validator: Validators::Size.new(:pixel, :keyword)
87
+
88
+ # Width of the box. Can be a percentage or pixel value.
89
+ # @!method width(value)
90
+ # @param value [String, nil] E.g., `"100px"`, `"50%"`.
91
+ # @return [String, nil] The current width.
28
92
  option :width, default: nil, validator: Validators::Size.new(:pixel, :percentage)
93
+
94
+ # Maximum width of the box.
95
+ # @!method max_width(value)
96
+ # @param value [String, nil] E.g., `"100px"`, `"50%"`.
97
+ # @return [String, nil] The current maximum width.
29
98
  option :max_width, default: nil, validator: Validators::Size.new(:pixel, :percentage)
99
+
100
+ # Height of the box.
101
+ # @!method height(value)
102
+ # @param value [String, nil] E.g., `"100px"`, `"50%"`.
103
+ # @return [String, nil] The current height.
30
104
  option :height, default: nil, validator: Validators::Size.new(:pixel, :percentage)
105
+
106
+ # Maximum height of the box.
107
+ # @!method max_height(value)
108
+ # @param value [String, nil] E.g., `"100px"`, `"50%"`.
109
+ # @return [String, nil] The current maximum height.
31
110
  option :max_height, default: nil, validator: Validators::Size.new(:pixel, :percentage)
32
111
 
112
+ # Initializes a new Flex Message Box component.
113
+ # The provided block is instance-eval'd, allowing DSL methods for adding
114
+ # child components (e.g., {#text}, {#button}, nested {#box}) to be called.
115
+ #
116
+ # @param context [Object, nil] An optional context for the builder.
117
+ # @param options [Hash] A hash of options to set instance variables.
118
+ # Corresponds to the `option` definitions in this class and included modules.
119
+ # @param block [Proc, nil] A block to define the contents of this box.
33
120
  def initialize(context: nil, **options, &)
34
- @contents = []
35
-
36
- super
121
+ @contents = [] # Holds child components
122
+ super # Calls Base#initialize, sets options, and evals block
37
123
  end
38
124
 
125
+ # Adds a nested Flex {Box} component to this box's contents.
126
+ #
127
+ # @param options [Hash] Options for the nested box. See {Box#initialize}.
128
+ # @param block [Proc] A block to define the contents of the nested box.
129
+ # @return [Flex::Box] The newly created nested Box object.
39
130
  def box(**options, &)
40
131
  @contents << Flex::Box.new(context: context, **options, &)
41
132
  end
42
133
 
134
+ # Adds a Flex {Text} component to this box's contents.
135
+ #
136
+ # @param text [String] The text content.
137
+ # @param options [Hash] Options for the text component. See {Text#initialize}.
138
+ # @param block [Proc, nil] An optional block for the text component (e.g., for an action).
139
+ # @return [Flex::Text] The newly created Text object.
43
140
  def text(text, **options, &)
44
141
  @contents << Flex::Text.new(text, context: context, **options, &)
45
142
  end
46
143
 
144
+ # Adds a Flex {Button} component to this box's contents.
145
+ #
146
+ # @param options [Hash] Options for the button component. See {Button#initialize}.
147
+ # @param block [Proc] A block to define the button's action and properties.
148
+ # @return [Flex::Button] The newly created Button object.
47
149
  def button(**options, &)
48
150
  @contents << Flex::Button.new(context: context, **options, &)
49
151
  end
50
152
 
153
+ # Adds a Flex {Image} component to this box's contents.
154
+ #
155
+ # @param url [String] The URL of the image.
156
+ # @param options [Hash] Options for the image component. See {Image#initialize}.
157
+ # @param block [Proc, nil] An optional block for the image component (e.g., for an action).
158
+ # @return [Flex::Image] The newly created Image object.
51
159
  def image(url, **options, &)
52
160
  @contents << Flex::Image.new(url, context: context, **options, &)
53
161
  end
54
162
 
55
- def to_h # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
163
+ def to_h
56
164
  raise RequiredError, "layout is required" if layout.nil?
57
165
 
166
+ return to_sdkv2 if context.sdkv2?
167
+
168
+ to_api
169
+ end
170
+
171
+ private
172
+
173
+ def to_api # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
58
174
  {
59
175
  type: "box",
60
176
  layout: layout,
61
- # Position
177
+ # Position & Layout
62
178
  justifyContent: justify_content,
63
179
  alignItems: align_items,
64
180
  spacing: spacing,
65
181
  # Position::Padding
66
- paddingAll: padding,
182
+ paddingAll: padding || padding_all,
67
183
  paddingTop: padding_top,
68
184
  paddingBottom: padding_bottom,
69
185
  paddingStart: padding_start,
@@ -83,8 +199,44 @@ module Line
83
199
  maxHeight: max_height,
84
200
  # Size::Flex
85
201
  flex: flex,
202
+ # Contents & Action
203
+ contents: contents.map(&:to_h),
204
+ action: action&.to_h # From Actionable module
205
+ }.compact
206
+ end
207
+
208
+ def to_sdkv2 # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
209
+ {
210
+ type: "box",
211
+ layout: layout,
212
+ # Position & Layout
213
+ justify_content: justify_content,
214
+ align_items: align_items,
215
+ spacing: spacing,
216
+ # Position::Padding
217
+ padding_all: padding || padding_all,
218
+ padding_top: padding_top,
219
+ padding_bottom: padding_bottom,
220
+ padding_start: padding_start,
221
+ padding_end: padding_end,
222
+ # Position::Margin
223
+ margin: margin,
224
+ # Position::Offset
225
+ position: position,
226
+ offset_top: offset_top,
227
+ offset_bottom: offset_bottom,
228
+ offset_start: offset_start,
229
+ offset_end: offset_end,
230
+ # Size
231
+ width: width,
232
+ max_width: max_width,
233
+ height: height,
234
+ max_height: max_height,
235
+ # Size::Flex
236
+ flex: flex,
237
+ # Contents & Action
86
238
  contents: contents.map(&:to_h),
87
- action: action&.to_h
239
+ action: action&.to_h # From Actionable module
88
240
  }.compact
89
241
  end
90
242
  end
@@ -4,51 +4,139 @@ module Line
4
4
  module Message
5
5
  module Builder
6
6
  module Flex
7
- # The bubble is container for the Flex message.
7
+ # Represents a "bubble" container in a LINE Flex Message.
8
+ # A bubble is a self-contained unit of content, structured into optional
9
+ # sections: header, hero (an image or box), body, and footer.
10
+ # Bubbles are the fundamental building blocks for single Flex Messages or
11
+ # for each item in a {Carousel} container.
12
+ #
13
+ # @example Creating a simple bubble with a body
14
+ # Line::Message::Builder.with do |root|
15
+ # root.flex alt_text: "Simple Bubble" do |flex|
16
+ # flex.bubble do |bubble|
17
+ # bubble.body do |body_box|
18
+ # body_box.text "Hello, this is a bubble!"
19
+ # end
20
+ # end
21
+ # end
22
+ # end
23
+ #
24
+ # @see https://developers.line.biz/en/reference/messaging-api/#bubble
25
+ # @see HasPartial For including reusable component groups within sections.
26
+ # @see Box For the structure of header, hero (if box), body, and footer.
27
+ # @see Image For using an image as a hero section.
8
28
  class Bubble < Line::Message::Builder::Base
9
- include HasPartial
29
+ include HasPartial # Allows including predefined partial component sets into sections.
10
30
 
11
- option :size, default: nil
31
+ # Specifies the size of the bubble.
32
+ # @!method size(value)
33
+ # @param value [Symbol, String, nil] Bubble size. Keywords: `:nano`, `:micro`,
34
+ # `:kilo`, `:mega`, `:giga`. Pixel/percentage values are not directly
35
+ # supported for bubble size by LINE; use keywords.
36
+ # @return [Symbol, String, nil] The current bubble size.
37
+ option :size, default: nil # E.g., :nano, :micro, :kilo, :mega, :giga
38
+
39
+ # Defines custom styles for the bubble and its sections (header, hero, body, footer).
40
+ # @!method styles(value)
41
+ # @param value [Hash, nil] A hash defining style overrides.
42
+ # See LINE API documentation for the structure of the styles object.
43
+ # @return [Hash, nil] The current styles hash.
44
+ # @example
45
+ # bubble.styles(
46
+ # header: { backgroundColor: "#FF0000" },
47
+ # body: { separator: true, separatorColor: "#00FF00" }
48
+ # )
12
49
  option :styles, default: nil
13
50
 
51
+ # Initializes a new Flex Message Bubble container.
52
+ # The provided block is instance-eval'd, allowing DSL methods for defining
53
+ # sections (e.g., {#header}, {#body}) to be called.
54
+ #
55
+ # @param context [Object, nil] An optional context for the builder.
56
+ # @param options [Hash] A hash of options to set instance variables
57
+ # (e.g., `:size`, `:styles`).
58
+ # @param block [Proc, nil] A block to define the sections of this bubble.
14
59
  def initialize(context: nil, **options, &)
15
60
  @header = nil
16
61
  @hero = nil
17
62
  @body = nil
18
63
  @footer = nil
19
64
 
20
- super
65
+ super # Calls Base#initialize, sets options, and evals block
21
66
  end
22
67
 
68
+ # Defines the header section of the bubble using a {Box} component.
69
+ #
70
+ # @param options [Hash] Options for the header Box. See {Box#initialize}.
71
+ # @param block [Proc] A block to define the contents of the header Box.
72
+ # @return [Flex::Box] The newly created Box object for the header.
23
73
  def header(**options, &)
24
74
  @header = Box.new(**options, context: context, &)
25
75
  end
26
76
 
77
+ # Defines the hero section of the bubble using a {Box} component.
78
+ # The hero section is typically used for prominent content like a large image or video.
79
+ #
80
+ # @param options [Hash] Options for the hero Box. See {Box#initialize}.
81
+ # @param block [Proc] A block to define the contents of the hero Box.
82
+ # @return [Flex::Box] The newly created Box object for the hero section.
27
83
  def hero(**options, &)
28
84
  @hero = Box.new(**options, context: context, &)
29
85
  end
30
86
 
87
+ # Defines the hero section of the bubble using an {Image} component.
88
+ # This is a convenience method for common cases where the hero is a single image.
89
+ #
90
+ # @param url [String] The URL of the image.
91
+ # @param options [Hash] Options for the Image component. See {Image#initialize}.
92
+ # @param block [Proc, nil] An optional block for the Image component (e.g., for an action).
93
+ # @return [Flex::Image] The newly created Image object for the hero section.
31
94
  def hero_image(url, **options, &)
32
95
  @hero = Image.new(url, **options, context: context, &)
33
96
  end
34
97
 
98
+ # Defines the body section of the bubble using a {Box} component.
99
+ # This is the main content area of the bubble.
100
+ #
101
+ # @param options [Hash] Options for the body Box. See {Box#initialize}.
102
+ # @param block [Proc] A block to define the contents of the body Box.
103
+ # @return [Flex::Box] The newly created Box object for the body.
35
104
  def body(**options, &)
36
105
  @body = Box.new(**options, context: context, &)
37
106
  end
38
107
 
108
+ # Defines the footer section of the bubble using a {Box} component.
109
+ #
110
+ # @param options [Hash] Options for the footer Box. See {Box#initialize}.
111
+ # @param block [Proc] A block to define the contents of the footer Box.
112
+ # @return [Flex::Box] The newly created Box object for the footer.
39
113
  def footer(**options, &)
40
114
  @footer = Box.new(**options, context: context, &)
41
115
  end
42
116
 
43
- def to_h
117
+ private
118
+
119
+ def to_api
120
+ {
121
+ type: "bubble",
122
+ size: size, # From option
123
+ styles: styles, # From option
124
+ header: @header&.to_h,
125
+ hero: @hero&.to_h,
126
+ body: @body&.to_h,
127
+ footer: @footer&.to_h
128
+ }.compact
129
+ end
130
+
131
+ def to_sdkv2
44
132
  {
45
133
  type: "bubble",
134
+ size: size, # From option
135
+ styles: styles, # From option
46
136
  header: @header&.to_h,
47
137
  hero: @hero&.to_h,
48
138
  body: @body&.to_h,
49
- footer: @footer&.to_h,
50
- size: size,
51
- styles: styles
139
+ footer: @footer&.to_h
52
140
  }.compact
53
141
  end
54
142
  end