block-kit 1.0.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.
- checksums.yaml +7 -0
- data/.rspec +5 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/LICENSE.txt +21 -0
- data/README.md +183 -0
- data/Rakefile +8 -0
- data/lib/block_kit/base.rb +190 -0
- data/lib/block_kit/blocks.rb +50 -0
- data/lib/block_kit/composition/confirmation_dialog.rb +48 -0
- data/lib/block_kit/composition/conversation_filter.rb +42 -0
- data/lib/block_kit/composition/dispatch_action_config.rb +33 -0
- data/lib/block_kit/composition/input_parameter.rb +19 -0
- data/lib/block_kit/composition/mrkdwn.rb +15 -0
- data/lib/block_kit/composition/option.rb +41 -0
- data/lib/block_kit/composition/option_group.rb +27 -0
- data/lib/block_kit/composition/overflow_option.rb +21 -0
- data/lib/block_kit/composition/plain_text.rb +15 -0
- data/lib/block_kit/composition/slack_file.rb +36 -0
- data/lib/block_kit/composition/text.rb +29 -0
- data/lib/block_kit/composition/trigger.rb +33 -0
- data/lib/block_kit/composition/workflow.rb +19 -0
- data/lib/block_kit/composition.rb +19 -0
- data/lib/block_kit/concerns/confirmable.rb +21 -0
- data/lib/block_kit/concerns/conversation_selection.rb +26 -0
- data/lib/block_kit/concerns/dispatchable.rb +24 -0
- data/lib/block_kit/concerns/dsl_generation.rb +76 -0
- data/lib/block_kit/concerns/external.rb +19 -0
- data/lib/block_kit/concerns/focusable_on_load.rb +17 -0
- data/lib/block_kit/concerns/has_initial_option.rb +23 -0
- data/lib/block_kit/concerns/has_initial_options.rb +23 -0
- data/lib/block_kit/concerns/has_option_groups.rb +37 -0
- data/lib/block_kit/concerns/has_options.rb +28 -0
- data/lib/block_kit/concerns/has_placeholder.rb +21 -0
- data/lib/block_kit/concerns/has_rich_text_elements.rb +83 -0
- data/lib/block_kit/concerns/plain_text_emoji_assignment.rb +39 -0
- data/lib/block_kit/concerns.rb +18 -0
- data/lib/block_kit/elements/base.rb +23 -0
- data/lib/block_kit/elements/base_button.rb +45 -0
- data/lib/block_kit/elements/button.rb +27 -0
- data/lib/block_kit/elements/channels_select.rb +22 -0
- data/lib/block_kit/elements/checkboxes.rb +14 -0
- data/lib/block_kit/elements/conversations_select.rb +24 -0
- data/lib/block_kit/elements/date_picker.rb +21 -0
- data/lib/block_kit/elements/datetime_picker.rb +21 -0
- data/lib/block_kit/elements/email_text_input.rb +24 -0
- data/lib/block_kit/elements/external_select.rb +21 -0
- data/lib/block_kit/elements/file_input.rb +33 -0
- data/lib/block_kit/elements/image.rb +53 -0
- data/lib/block_kit/elements/multi_channels_select.rb +31 -0
- data/lib/block_kit/elements/multi_conversations_select.rb +33 -0
- data/lib/block_kit/elements/multi_external_select.rb +21 -0
- data/lib/block_kit/elements/multi_select.rb +21 -0
- data/lib/block_kit/elements/multi_static_select.rb +12 -0
- data/lib/block_kit/elements/multi_users_select.rb +31 -0
- data/lib/block_kit/elements/number_input.rb +52 -0
- data/lib/block_kit/elements/overflow.rb +23 -0
- data/lib/block_kit/elements/plain_text_input.rb +66 -0
- data/lib/block_kit/elements/radio_buttons.rb +14 -0
- data/lib/block_kit/elements/rich_text_input.rb +26 -0
- data/lib/block_kit/elements/select.rb +18 -0
- data/lib/block_kit/elements/static_select.rb +12 -0
- data/lib/block_kit/elements/time_picker.rb +43 -0
- data/lib/block_kit/elements/url_text_input.rb +24 -0
- data/lib/block_kit/elements/users_select.rb +17 -0
- data/lib/block_kit/elements/workflow_button.rb +18 -0
- data/lib/block_kit/elements.rb +36 -0
- data/lib/block_kit/fixers/associated.rb +27 -0
- data/lib/block_kit/fixers/base.rb +30 -0
- data/lib/block_kit/fixers/null_value.rb +42 -0
- data/lib/block_kit/fixers/truncate.rb +35 -0
- data/lib/block_kit/fixers.rb +11 -0
- data/lib/block_kit/layout/actions.rb +84 -0
- data/lib/block_kit/layout/base.rb +23 -0
- data/lib/block_kit/layout/context.rb +48 -0
- data/lib/block_kit/layout/divider.rb +9 -0
- data/lib/block_kit/layout/file.rb +19 -0
- data/lib/block_kit/layout/header.rb +22 -0
- data/lib/block_kit/layout/image.rb +61 -0
- data/lib/block_kit/layout/input.rb +119 -0
- data/lib/block_kit/layout/markdown.rb +19 -0
- data/lib/block_kit/layout/rich_text/elements/broadcast.rb +22 -0
- data/lib/block_kit/layout/rich_text/elements/channel.rb +21 -0
- data/lib/block_kit/layout/rich_text/elements/color.rb +20 -0
- data/lib/block_kit/layout/rich_text/elements/date.rb +34 -0
- data/lib/block_kit/layout/rich_text/elements/emoji.rb +27 -0
- data/lib/block_kit/layout/rich_text/elements/link.rb +28 -0
- data/lib/block_kit/layout/rich_text/elements/mention_style.rb +27 -0
- data/lib/block_kit/layout/rich_text/elements/text.rb +23 -0
- data/lib/block_kit/layout/rich_text/elements/text_style.rb +23 -0
- data/lib/block_kit/layout/rich_text/elements/user.rb +21 -0
- data/lib/block_kit/layout/rich_text/elements/usergroup.rb +21 -0
- data/lib/block_kit/layout/rich_text/elements.rb +35 -0
- data/lib/block_kit/layout/rich_text/list.rb +41 -0
- data/lib/block_kit/layout/rich_text/preformatted.rb +19 -0
- data/lib/block_kit/layout/rich_text/quote.rb +19 -0
- data/lib/block_kit/layout/rich_text/section.rb +11 -0
- data/lib/block_kit/layout/rich_text.rb +53 -0
- data/lib/block_kit/layout/section.rb +152 -0
- data/lib/block_kit/layout/video.rb +71 -0
- data/lib/block_kit/layout.rb +35 -0
- data/lib/block_kit/surfaces/base.rb +173 -0
- data/lib/block_kit/surfaces/home.rb +40 -0
- data/lib/block_kit/surfaces/message.rb +140 -0
- data/lib/block_kit/surfaces/modal.rb +74 -0
- data/lib/block_kit/surfaces.rb +11 -0
- data/lib/block_kit/typed_array.rb +114 -0
- data/lib/block_kit/typed_set.rb +78 -0
- data/lib/block_kit/types/array.rb +45 -0
- data/lib/block_kit/types/blocks.rb +37 -0
- data/lib/block_kit/types/generic.rb +45 -0
- data/lib/block_kit/types/option.rb +48 -0
- data/lib/block_kit/types/set.rb +35 -0
- data/lib/block_kit/types/text.rb +80 -0
- data/lib/block_kit/types.rb +17 -0
- data/lib/block_kit/validators/array_inclusion_validator.rb +55 -0
- data/lib/block_kit/validators/associated_validator.rb +61 -0
- data/lib/block_kit/validators.rb +8 -0
- data/lib/block_kit/version.rb +5 -0
- data/lib/block_kit.rb +38 -0
- metadata +190 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Concerns
|
5
|
+
module HasPlaceholder
|
6
|
+
MAX_TEXT_LENGTH = 150
|
7
|
+
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
plain_text_attribute :placeholder
|
12
|
+
validates :placeholder, presence: true, length: {maximum: MAX_TEXT_LENGTH}, allow_nil: true
|
13
|
+
fixes :placeholder, truncate: {maximum: MAX_TEXT_LENGTH}, null_value: {error_types: [:blank]}
|
14
|
+
end
|
15
|
+
|
16
|
+
def as_json(*)
|
17
|
+
super.merge(placeholder: placeholder&.as_json).compact
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Concerns
|
5
|
+
module HasRichTextElements
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
attribute :elements, Types::Array.of(Types::Blocks.new(*Layout::RichText::Elements.all))
|
10
|
+
validates :elements, presence: true, "block_kit/validators/associated": true
|
11
|
+
fixes :elements, associated: true
|
12
|
+
|
13
|
+
dsl_method :elements, as: :broadcast, type: Layout::RichText::Elements::Broadcast, required_fields: [:range], yields: false
|
14
|
+
dsl_method :elements, as: :color, type: Layout::RichText::Elements::Color, required_fields: [:value], yields: false
|
15
|
+
dsl_method :elements, as: :date, type: Layout::RichText::Elements::Date, required_fields: [:timestamp, :format], yields: false
|
16
|
+
dsl_method :elements, as: :emoji, type: Layout::RichText::Elements::Emoji, required_fields: [:name], yields: false
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(attributes = {})
|
20
|
+
attributes = attributes.with_indifferent_access
|
21
|
+
attributes[:elements] ||= []
|
22
|
+
|
23
|
+
super
|
24
|
+
end
|
25
|
+
|
26
|
+
def channel(channel_id:, styles: [])
|
27
|
+
style = if styles.present?
|
28
|
+
styles = Array(styles).map { |s| [s.to_s, true] }.to_h
|
29
|
+
Layout::RichText::Elements::MentionStyle.new(**styles.slice(*Layout::RichText::Elements::MentionStyle.attribute_names))
|
30
|
+
end
|
31
|
+
|
32
|
+
append(Layout::RichText::Elements::Channel.new(channel_id: channel_id, style: style))
|
33
|
+
end
|
34
|
+
|
35
|
+
def link(url:, text:, unsafe: nil, styles: [])
|
36
|
+
style = if styles.present?
|
37
|
+
styles = Array(styles).map { |s| [s.to_s, true] }.to_h
|
38
|
+
Layout::RichText::Elements::TextStyle.new(**styles.slice(*Layout::RichText::Elements::TextStyle.attribute_names))
|
39
|
+
end
|
40
|
+
|
41
|
+
append(Layout::RichText::Elements::Link.new(url: url, text: text, unsafe: unsafe, style: style))
|
42
|
+
end
|
43
|
+
|
44
|
+
def text(text:, styles: [])
|
45
|
+
style = if styles.present?
|
46
|
+
styles = Array(styles).map { |s| [s.to_s, true] }.to_h
|
47
|
+
Layout::RichText::Elements::TextStyle.new(**styles.slice(*Layout::RichText::Elements::TextStyle.attribute_names))
|
48
|
+
end
|
49
|
+
|
50
|
+
append(Layout::RichText::Elements::Text.new(text: text, style: style))
|
51
|
+
end
|
52
|
+
|
53
|
+
def usergroup(usergroup_id:, styles: [])
|
54
|
+
style = if styles.present?
|
55
|
+
styles = Array(styles).map { |s| [s.to_s, true] }.to_h
|
56
|
+
Layout::RichText::Elements::MentionStyle.new(**styles.slice(*Layout::RichText::Elements::MentionStyle.attribute_names))
|
57
|
+
end
|
58
|
+
|
59
|
+
append(Layout::RichText::Elements::Usergroup.new(usergroup_id: usergroup_id, style: style))
|
60
|
+
end
|
61
|
+
alias_method :user_group, :usergroup
|
62
|
+
|
63
|
+
def user(user_id:, styles: [])
|
64
|
+
style = if styles.present?
|
65
|
+
styles = Array(styles).map { |s| [s.to_s, true] }.to_h
|
66
|
+
Layout::RichText::Elements::MentionStyle.new(**styles.slice(*Layout::RichText::Elements::MentionStyle.attribute_names))
|
67
|
+
end
|
68
|
+
|
69
|
+
append(Layout::RichText::Elements::User.new(user_id: user_id, style: style))
|
70
|
+
end
|
71
|
+
|
72
|
+
def append(element)
|
73
|
+
elements << element
|
74
|
+
|
75
|
+
self
|
76
|
+
end
|
77
|
+
|
78
|
+
def as_json(*)
|
79
|
+
super.merge(elements: elements&.map(&:as_json)).compact
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Concerns
|
5
|
+
module PlainTextEmojiAssignment
|
6
|
+
def self.new(*attributes)
|
7
|
+
Module.new do
|
8
|
+
define_method(:initialize) do |attrs = {}|
|
9
|
+
raise ArgumentError, "expected `attributes' to be a Hash, got #{attrs.class}" unless attrs.is_a?(Hash)
|
10
|
+
|
11
|
+
emoji = attrs.delete(:emoji)
|
12
|
+
|
13
|
+
super(attrs)
|
14
|
+
|
15
|
+
unless emoji.nil?
|
16
|
+
attributes.each do |attribute|
|
17
|
+
text = public_send(attribute)
|
18
|
+
public_send(:"#{attribute}=", Composition::PlainText.new) if text.nil?
|
19
|
+
public_send(attribute).emoji = emoji
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
attributes.each do |attribute|
|
25
|
+
define_method(:"#{attribute}=") do |value|
|
26
|
+
# Attempt to preserve the existing text object's emoji attribute if present
|
27
|
+
text = public_send(attribute)
|
28
|
+
if !text&.emoji.nil? && value.is_a?(String)
|
29
|
+
value = Composition::PlainText.new(text: value, emoji: text.emoji)
|
30
|
+
end
|
31
|
+
|
32
|
+
super(value)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Concerns
|
5
|
+
autoload :Confirmable, "block_kit/concerns/confirmable"
|
6
|
+
autoload :ConversationSelection, "block_kit/concerns/conversation_selection"
|
7
|
+
autoload :Dispatchable, "block_kit/concerns/dispatchable"
|
8
|
+
autoload :External, "block_kit/concerns/external"
|
9
|
+
autoload :FocusableOnLoad, "block_kit/concerns/focusable_on_load"
|
10
|
+
autoload :HasInitialOption, "block_kit/concerns/has_initial_option"
|
11
|
+
autoload :HasInitialOptions, "block_kit/concerns/has_initial_options"
|
12
|
+
autoload :HasOptions, "block_kit/concerns/has_options"
|
13
|
+
autoload :HasOptionGroups, "block_kit/concerns/has_option_groups"
|
14
|
+
autoload :HasPlaceholder, "block_kit/concerns/has_placeholder"
|
15
|
+
autoload :HasRichTextElements, "block_kit/concerns/has_rich_text_elements"
|
16
|
+
autoload :PlainTextEmojiAssignment, "block_kit/concerns/plain_text_emoji_assignment"
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Elements
|
5
|
+
class Base < BlockKit::Base
|
6
|
+
MAX_ACTION_ID_LENGTH = 255
|
7
|
+
|
8
|
+
attribute :action_id, :string
|
9
|
+
validates :action_id, presence: true, length: {maximum: MAX_ACTION_ID_LENGTH}, allow_nil: true
|
10
|
+
fixes :action_id, truncate: {maximum: MAX_ACTION_ID_LENGTH, dangerous: true, omission: ""}, null_value: {error_types: [:blank]}
|
11
|
+
|
12
|
+
def initialize(attributes = {})
|
13
|
+
raise NotImplementedError, "#{self.class} is an abstract class and can't be instantiated." if instance_of?(Base)
|
14
|
+
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def as_json(*)
|
19
|
+
super.merge(action_id: action_id).compact
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Elements
|
5
|
+
class BaseButton < Base
|
6
|
+
self.type = :button
|
7
|
+
|
8
|
+
MAX_TEXT_LENGTH = 75
|
9
|
+
MAX_ACCESSIBILITY_LABEL_LENGTH = 75
|
10
|
+
VALID_STYLES = [
|
11
|
+
PRIMARY = "primary",
|
12
|
+
DANGER = "danger"
|
13
|
+
].freeze
|
14
|
+
|
15
|
+
plain_text_attribute :text
|
16
|
+
attribute :style, :string
|
17
|
+
attribute :accessibility_label, :string
|
18
|
+
|
19
|
+
include Concerns::PlainTextEmojiAssignment.new(:text)
|
20
|
+
|
21
|
+
validates :text, presence: true, length: {maximum: MAX_TEXT_LENGTH}
|
22
|
+
fixes :text, truncate: {maximum: MAX_TEXT_LENGTH}
|
23
|
+
|
24
|
+
validates :style, presence: true, inclusion: {in: VALID_STYLES}, allow_nil: true
|
25
|
+
fixes :style, null_value: {error_types: [:inclusion]}
|
26
|
+
|
27
|
+
validates :accessibility_label, presence: true, length: {maximum: MAX_ACCESSIBILITY_LABEL_LENGTH}, allow_nil: true
|
28
|
+
fixes :accessibility_label, truncate: {maximum: MAX_ACCESSIBILITY_LABEL_LENGTH}, null_value: {error_types: [:blank]}
|
29
|
+
|
30
|
+
def initialize(attributes = {})
|
31
|
+
raise NotImplementedError, "#{self.class} is an abstract class and can't be instantiated." if instance_of?(BaseButton)
|
32
|
+
|
33
|
+
super
|
34
|
+
end
|
35
|
+
|
36
|
+
def as_json(*)
|
37
|
+
super.merge(
|
38
|
+
text: text&.as_json,
|
39
|
+
style: style,
|
40
|
+
accessibility_label: accessibility_label
|
41
|
+
).compact
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "uri"
|
4
|
+
|
5
|
+
module BlockKit
|
6
|
+
module Elements
|
7
|
+
class Button < BaseButton
|
8
|
+
MAX_URL_LENGTH = 3000
|
9
|
+
MAX_VALUE_LENGTH = 2000
|
10
|
+
|
11
|
+
include Concerns::Confirmable
|
12
|
+
|
13
|
+
attribute :url, :string
|
14
|
+
attribute :value, :string
|
15
|
+
|
16
|
+
validates :url, presence: true, format: {with: URI::DEFAULT_PARSER.make_regexp, message: "is not a valid URI", allow_blank: true}, length: {maximum: MAX_URL_LENGTH}, allow_nil: true
|
17
|
+
fixes :url, truncate: {maximum: MAX_URL_LENGTH, dangerous: true, omission: ""}, null_value: {error_types: [:blank]}
|
18
|
+
|
19
|
+
validates :value, presence: true, length: {maximum: MAX_VALUE_LENGTH}, allow_nil: true
|
20
|
+
fixes :value, truncate: {maximum: MAX_VALUE_LENGTH, dangerous: true, omission: ""}, null_value: {error_types: [:blank]}
|
21
|
+
|
22
|
+
def as_json(*)
|
23
|
+
super.merge(url: url, value: value).compact
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Elements
|
5
|
+
class ChannelsSelect < Select
|
6
|
+
self.type = :channels_select
|
7
|
+
|
8
|
+
attribute :initial_channel, :string
|
9
|
+
attribute :response_url_enabled, :boolean
|
10
|
+
|
11
|
+
validates :initial_channel, presence: true, allow_nil: true
|
12
|
+
fixes :initial_channel, null_value: {error_types: [:blank]}
|
13
|
+
|
14
|
+
def as_json(*)
|
15
|
+
super.merge(
|
16
|
+
initial_channel: initial_channel,
|
17
|
+
response_url_enabled: response_url_enabled
|
18
|
+
).compact
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Elements
|
5
|
+
class Checkboxes < Base
|
6
|
+
self.type = :checkboxes
|
7
|
+
|
8
|
+
include Concerns::Confirmable
|
9
|
+
include Concerns::HasOptions.new(limit: 10)
|
10
|
+
include Concerns::HasInitialOptions
|
11
|
+
include Concerns::FocusableOnLoad
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Elements
|
5
|
+
class ConversationsSelect < Select
|
6
|
+
self.type = :conversations_select
|
7
|
+
|
8
|
+
include Concerns::ConversationSelection
|
9
|
+
|
10
|
+
attribute :initial_conversation, :string
|
11
|
+
attribute :response_url_enabled, :boolean
|
12
|
+
|
13
|
+
validates :initial_conversation, presence: true, allow_nil: true
|
14
|
+
fixes :initial_conversation, null_value: {error_types: [:blank]}
|
15
|
+
|
16
|
+
def as_json(*)
|
17
|
+
super.merge(
|
18
|
+
initial_conversation: initial_conversation,
|
19
|
+
response_url_enabled: response_url_enabled
|
20
|
+
).compact
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Elements
|
5
|
+
class DatePicker < Base
|
6
|
+
self.type = :datepicker
|
7
|
+
|
8
|
+
include Concerns::Confirmable
|
9
|
+
include Concerns::FocusableOnLoad
|
10
|
+
include Concerns::HasPlaceholder
|
11
|
+
include Concerns::PlainTextEmojiAssignment.new(:placeholder)
|
12
|
+
|
13
|
+
attribute :initial_date, :date
|
14
|
+
validates :initial_date, presence: true, allow_nil: true
|
15
|
+
|
16
|
+
def as_json(*)
|
17
|
+
super.merge(initial_date: initial_date&.iso8601).compact
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Elements
|
5
|
+
class DatetimePicker < Base
|
6
|
+
self.type = :datetimepicker
|
7
|
+
|
8
|
+
include Concerns::Confirmable
|
9
|
+
include Concerns::FocusableOnLoad
|
10
|
+
|
11
|
+
attribute :initial_date_time, :datetime
|
12
|
+
validates :initial_date_time, presence: true, allow_nil: true
|
13
|
+
|
14
|
+
alias_attribute :initial_datetime, :initial_date_time
|
15
|
+
|
16
|
+
def as_json(*)
|
17
|
+
super.merge(initial_date_time: initial_date_time&.to_i).compact
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "uri"
|
4
|
+
|
5
|
+
module BlockKit
|
6
|
+
module Elements
|
7
|
+
class EmailTextInput < Base
|
8
|
+
self.type = :email_text_input
|
9
|
+
|
10
|
+
include Concerns::Dispatchable
|
11
|
+
include Concerns::FocusableOnLoad
|
12
|
+
include Concerns::HasPlaceholder
|
13
|
+
include Concerns::PlainTextEmojiAssignment.new(:placeholder)
|
14
|
+
|
15
|
+
attribute :initial_value, :string
|
16
|
+
validates :initial_value, presence: true, format: {with: URI::MailTo::EMAIL_REGEXP}, allow_nil: true
|
17
|
+
fixes :initial_value, null_value: {error_types: [:blank, :invalid]}
|
18
|
+
|
19
|
+
def as_json(*)
|
20
|
+
super.merge(initial_value: initial_value).compact
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Elements
|
5
|
+
class ExternalSelect < Select
|
6
|
+
self.type = :external_select
|
7
|
+
|
8
|
+
include Concerns::External
|
9
|
+
|
10
|
+
attribute :initial_option, Types::Generic.of_type(Composition::Option)
|
11
|
+
validates :initial_option, "block_kit/validators/associated": true, allow_nil: true
|
12
|
+
fixes :initial_option, associated: true
|
13
|
+
|
14
|
+
dsl_method :initial_option, required_fields: [:text, :value], yields: false
|
15
|
+
|
16
|
+
def as_json(*)
|
17
|
+
super.merge(initial_option: initial_option&.as_json).compact
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/object/inclusion" # Necessary for `max_files.in?(1..10)`
|
4
|
+
|
5
|
+
module BlockKit
|
6
|
+
module Elements
|
7
|
+
class FileInput < Base
|
8
|
+
self.type = :file_input
|
9
|
+
|
10
|
+
VALID_MAX_FILES_RANGE = 1..10
|
11
|
+
|
12
|
+
attribute :filetypes, Types::Set.of(:string)
|
13
|
+
attribute :max_files, :integer
|
14
|
+
|
15
|
+
validates :filetypes, presence: true, allow_nil: true
|
16
|
+
validates :max_files, presence: true, numericality: {in: VALID_MAX_FILES_RANGE, only_integer: true}, allow_nil: true
|
17
|
+
fix :clamp_max_files
|
18
|
+
|
19
|
+
def as_json(*)
|
20
|
+
super.merge(
|
21
|
+
filetypes: filetypes&.to_a,
|
22
|
+
max_files: max_files
|
23
|
+
).compact
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def clamp_max_files
|
29
|
+
self.max_files = max_files&.clamp(VALID_MAX_FILES_RANGE)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "uri"
|
4
|
+
|
5
|
+
module BlockKit
|
6
|
+
module Elements
|
7
|
+
class Image < BlockKit::Base
|
8
|
+
self.type = :image
|
9
|
+
|
10
|
+
MAX_ALT_TEXT_LENGTH = 2000
|
11
|
+
MAX_IMAGE_URL_LENGTH = 3000
|
12
|
+
|
13
|
+
attribute :alt_text, :string
|
14
|
+
attribute :image_url, :string
|
15
|
+
attribute :slack_file, Types::Generic.of_type(Composition::SlackFile)
|
16
|
+
|
17
|
+
validates :alt_text, presence: true, length: {maximum: MAX_ALT_TEXT_LENGTH}
|
18
|
+
fixes :alt_text, truncate: {maximum: MAX_ALT_TEXT_LENGTH}
|
19
|
+
|
20
|
+
validates :image_url, presence: true, length: {maximum: MAX_IMAGE_URL_LENGTH}, format: {with: URI::DEFAULT_PARSER.make_regexp(%w[http https]), message: "is not a valid URI", allow_blank: true}, allow_nil: true
|
21
|
+
fixes :image_url, truncate: {maximum: MAX_IMAGE_URL_LENGTH, dangerous: true, omission: ""}, null_value: {error_types: [:blank]}
|
22
|
+
|
23
|
+
validate :slack_file_or_url_present
|
24
|
+
|
25
|
+
def slack_file(attrs = {})
|
26
|
+
attrs = attrs.with_indifferent_access
|
27
|
+
return super() unless attrs.key?(:id) || attrs.key?(:url)
|
28
|
+
|
29
|
+
raise ArgumentError, "mutually exclusive keywords: :id, :url" if attrs.key?(:id) && attrs.key?(:url)
|
30
|
+
|
31
|
+
self.slack_file = Composition::SlackFile.new(**attrs.slice(:id, :url))
|
32
|
+
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def as_json(*)
|
37
|
+
super.merge(
|
38
|
+
alt_text: alt_text,
|
39
|
+
image_url: image_url,
|
40
|
+
slack_file: slack_file&.as_json
|
41
|
+
).compact
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def slack_file_or_url_present
|
47
|
+
if (slack_file.nil? && image_url.nil?) || (slack_file.present? && image_url.present?)
|
48
|
+
errors.add(:base, "must have either a slack_file or image_url but not both")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Elements
|
5
|
+
class MultiChannelsSelect < MultiSelect
|
6
|
+
self.type = :multi_channels_select
|
7
|
+
|
8
|
+
attribute :initial_channels, Types::Set.of(:string)
|
9
|
+
validate :initial_channels_are_all_present
|
10
|
+
fix :remove_blank_initial_channels
|
11
|
+
|
12
|
+
def as_json(*)
|
13
|
+
super.merge(initial_channels: initial_channels&.to_a).compact
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def initial_channels_are_all_present
|
19
|
+
if Array(initial_channels).any?(&:blank?)
|
20
|
+
errors.add(:initial_channels, "must not contain blank values")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def remove_blank_initial_channels
|
25
|
+
return unless initial_channels.present?
|
26
|
+
|
27
|
+
initial_channels.reject!(&:blank?)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Elements
|
5
|
+
class MultiConversationsSelect < MultiSelect
|
6
|
+
self.type = :multi_conversations_select
|
7
|
+
|
8
|
+
include Concerns::ConversationSelection
|
9
|
+
|
10
|
+
attribute :initial_conversations, Types::Set.of(:string)
|
11
|
+
validate :initial_conversations_are_all_present
|
12
|
+
fix :remove_blank_initial_conversations
|
13
|
+
|
14
|
+
def as_json(*)
|
15
|
+
super.merge(initial_conversations: initial_conversations&.to_a).compact
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def initial_conversations_are_all_present
|
21
|
+
if Array(initial_conversations).any?(&:blank?)
|
22
|
+
errors.add(:initial_conversations, "must not contain blank values")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def remove_blank_initial_conversations
|
27
|
+
return unless initial_conversations.present?
|
28
|
+
|
29
|
+
initial_conversations.reject!(&:blank?)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Elements
|
5
|
+
class MultiExternalSelect < MultiSelect
|
6
|
+
self.type = :multi_external_select
|
7
|
+
|
8
|
+
include Concerns::External
|
9
|
+
|
10
|
+
attribute :initial_options, Types::Array.of(Composition::Option)
|
11
|
+
validates :initial_options, "block_kit/validators/associated": true, allow_nil: true
|
12
|
+
fixes :initial_options, associated: true
|
13
|
+
|
14
|
+
dsl_method :initial_options, as: :initial_option, required_fields: [:text, :value], yields: false
|
15
|
+
|
16
|
+
def as_json(*)
|
17
|
+
super.merge(initial_options: initial_options&.map(&:as_json)).compact
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Elements
|
5
|
+
class MultiSelect < Select
|
6
|
+
attribute :max_selected_items, :integer
|
7
|
+
validates :max_selected_items, presence: true, numericality: {only_integer: true, greater_than: 0}, allow_nil: true
|
8
|
+
fixes :max_selected_items, null_value: {error_types: [:greater_than]}
|
9
|
+
|
10
|
+
def initialize(attributes = {})
|
11
|
+
raise NotImplementedError, "#{self.class} is an abstract class and can't be instantiated." if instance_of?(MultiSelect)
|
12
|
+
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
def as_json(*)
|
17
|
+
super.merge(max_selected_items: max_selected_items).compact
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Elements
|
5
|
+
class MultiStaticSelect < MultiSelect
|
6
|
+
self.type = :multi_static_select
|
7
|
+
|
8
|
+
include Concerns::HasOptionGroups.new(limit: 100, options_limit: 100)
|
9
|
+
include Concerns::HasInitialOptions
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Elements
|
5
|
+
class MultiUsersSelect < MultiSelect
|
6
|
+
self.type = :multi_users_select
|
7
|
+
|
8
|
+
attribute :initial_users, Types::Set.of(:string)
|
9
|
+
validate :initial_users_are_all_present
|
10
|
+
fix :remove_blank_initial_users
|
11
|
+
|
12
|
+
def as_json(*)
|
13
|
+
super.merge(initial_users: initial_users&.to_a).compact
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def initial_users_are_all_present
|
19
|
+
if Array(initial_users).any?(&:blank?)
|
20
|
+
errors.add(:initial_users, "must not contain blank values")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def remove_blank_initial_users
|
25
|
+
return unless initial_users.present?
|
26
|
+
|
27
|
+
initial_users.reject!(&:blank?)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|