fluxbit_view_components 0.1.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.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +20 -0
  3. data/README.md +86 -0
  4. data/app/components/fluxbit/alert_component.rb +126 -0
  5. data/app/components/fluxbit/avatar_component.rb +113 -0
  6. data/app/components/fluxbit/avatar_group_component.rb +23 -0
  7. data/app/components/fluxbit/badge_component.rb +79 -0
  8. data/app/components/fluxbit/button_component.rb +97 -0
  9. data/app/components/fluxbit/button_group_component.rb +43 -0
  10. data/app/components/fluxbit/card_component.rb +135 -0
  11. data/app/components/fluxbit/component.rb +86 -0
  12. data/app/components/fluxbit/flex_component.rb +93 -0
  13. data/app/components/fluxbit/form/checkbox_input_component.rb +61 -0
  14. data/app/components/fluxbit/form/component.rb +71 -0
  15. data/app/components/fluxbit/form/datepicker_component.rb +7 -0
  16. data/app/components/fluxbit/form/form_builder_component.rb +117 -0
  17. data/app/components/fluxbit/form/helper_text_component.rb +29 -0
  18. data/app/components/fluxbit/form/label_component.rb +65 -0
  19. data/app/components/fluxbit/form/radio_input_component.rb +21 -0
  20. data/app/components/fluxbit/form/range_input_component.rb +51 -0
  21. data/app/components/fluxbit/form/select_free_input_component.rb +77 -0
  22. data/app/components/fluxbit/form/select_input_component.rb +21 -0
  23. data/app/components/fluxbit/form/spacer_input_component.rb +12 -0
  24. data/app/components/fluxbit/form/text_input_component.rb +225 -0
  25. data/app/components/fluxbit/form/textarea_input_component.rb +57 -0
  26. data/app/components/fluxbit/form/toggle_input_component.rb +166 -0
  27. data/app/components/fluxbit/form/upload_image_input_component.html.erb +48 -0
  28. data/app/components/fluxbit/form/upload_image_input_component.rb +66 -0
  29. data/app/components/fluxbit/form/upload_input_component.html.erb +12 -0
  30. data/app/components/fluxbit/form/upload_input_component.rb +47 -0
  31. data/app/components/fluxbit/gravatar_component.rb +99 -0
  32. data/app/components/fluxbit/heading_component.rb +47 -0
  33. data/app/components/fluxbit/modal_component.rb +141 -0
  34. data/app/components/fluxbit/popover_component.rb +71 -0
  35. data/app/components/fluxbit/tab_component.rb +142 -0
  36. data/app/components/fluxbit/text_component.rb +36 -0
  37. data/app/components/fluxbit/tooltip_component.rb +38 -0
  38. data/app/helpers/fluxbit/classes_helper.rb +21 -0
  39. data/app/helpers/fluxbit/components_helper.rb +75 -0
  40. data/config/deploy.yml +37 -0
  41. data/config/locales/en.yml +6 -0
  42. data/lib/fluxbit/config/alert_component.rb +59 -0
  43. data/lib/fluxbit/config/avatar_component.rb +79 -0
  44. data/lib/fluxbit/config/badge_component.rb +77 -0
  45. data/lib/fluxbit/config/button_component.rb +86 -0
  46. data/lib/fluxbit/config/card_component.rb +32 -0
  47. data/lib/fluxbit/config/flex_component.rb +63 -0
  48. data/lib/fluxbit/config/form/helper_text_component.rb +20 -0
  49. data/lib/fluxbit/config/gravatar_component.rb +19 -0
  50. data/lib/fluxbit/config/heading_component.rb +39 -0
  51. data/lib/fluxbit/config/modal_component.rb +71 -0
  52. data/lib/fluxbit/config/paragraph_component.rb +11 -0
  53. data/lib/fluxbit/config/popover_component.rb +33 -0
  54. data/lib/fluxbit/config/tab_component.rb +131 -0
  55. data/lib/fluxbit/config/text_component.rb +110 -0
  56. data/lib/fluxbit/config/tooltip_component.rb +11 -0
  57. data/lib/fluxbit/view_components/codemods/v3_slot_setters.rb +222 -0
  58. data/lib/fluxbit/view_components/engine.rb +36 -0
  59. data/lib/fluxbit/view_components/version.rb +7 -0
  60. data/lib/fluxbit/view_components.rb +30 -0
  61. data/lib/fluxbit_view_components.rb +3 -0
  62. data/lib/install/install.rb +64 -0
  63. data/lib/tasks/fluxbit_view_components_tasks.rake +22 -0
  64. metadata +238 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 16de56192c4f954e22b5391bbd8e94970a8345ede43ffaf8866050222a8d486a
4
+ data.tar.gz: fb3a6c03297fc0836ee3c61005e530cc32640e0c97dbc872945b661e6e2b95c6
5
+ SHA512:
6
+ metadata.gz: 9127a488dc9bec5cc886886855edb7649e62a9ce36406e0c9aef4f956eb392fd9f21f42473459885eeb9758f19528c3185bd8729398852d91e17a27168a6cb40
7
+ data.tar.gz: 3bced6391398780c7877563c35f406ede7c4dc838c2ac9e410c99db815c1c58686d79aa034eff979c561010f191701329a7daa9c857ed06b521b6441ff9c502d
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2021 Dan Gamble
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,86 @@
1
+ # Fluxbit ViewComponents
2
+
3
+ Fluxbit ViewComponents is an implementation of the Fluxbit Design System using [ViewComponent](https://github.com/github/view_component).
4
+
5
+ <div style="text-align: center;">
6
+ <img src="docs/fluxbit.png" alt="Fluxbit ViewComponents" width="300" />
7
+ </div>
8
+
9
+ ## Preview
10
+
11
+ I don't have a site to host the lookbook. Sorry.
12
+
13
+ ## Usage
14
+
15
+ Render Fluxbit ViewComponents:
16
+
17
+ ```erb
18
+ <%= fx_card(title: "Title") do %>
19
+ <p>Card example</p>
20
+ <% end %>
21
+ ```
22
+
23
+ ## Dependencies
24
+
25
+ - [Anyicon](https://github.com/arthurmolina/anyicon)
26
+
27
+ ## Installation
28
+
29
+ Add `fluxbit_view_components` to your Gemfile:
30
+
31
+ ```bash
32
+ bundle add fluxbit_view_components
33
+ ```
34
+
35
+ Run installer:
36
+ ```bash
37
+ bin/rails fluxbit_view_components:install
38
+ ```
39
+
40
+ ## Development
41
+
42
+ To get started:
43
+
44
+ 1. Run: `bundle install`
45
+ 2. Run: `yarn install`
46
+ 3. Run: `bin/dev`
47
+
48
+ It will open demo app with component previews on `localhost:3000`. You can change components and they will be updated on page reload. Component previews located in `demo/test/components/previews`.
49
+
50
+ ![Lookbook](/docs/lookbook.png)
51
+
52
+ To run tests:
53
+
54
+ ```bash
55
+ rake
56
+ ```
57
+
58
+ ## Releases
59
+
60
+ The library follows [semantic versioning](https://semver.org/). To draft a new release you need to run `bin/release` with a new version number:
61
+
62
+ ```bash
63
+ bin/release VERSION
64
+ ```
65
+
66
+ Where the VERSION is the version number you want to release. This script will update the version in the gem and push it to GitHub and Rubygems automatically.
67
+
68
+ To release a new version of npm package update the package.json file with the new version number and run:
69
+
70
+ ```bash
71
+ npm run release
72
+ ```
73
+
74
+ After that make sure to commit changes in package.json.
75
+
76
+ ## Documentation
77
+
78
+ For more information, check out the following resources:
79
+
80
+ - [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md): Guidelines for contributing to this project and fostering a welcoming community.
81
+ - [CONTRIBUTING.md](CONTRIBUTING.md): Instructions on how to contribute to the project, including setting up your environment and submitting changes.
82
+ - [PULL_REQUEST_TEMPLATE.md](PULL_REQUEST_TEMPLATE.md): Template for submitting pull requests to ensure consistency and quality.
83
+
84
+ ## License
85
+
86
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##
4
+ # The `Fluxbit::AlertComponent` is a customizable alert component that extends `Fluxbit::Component`.
5
+ # It provides various options to display alert messages with different styles, icons, and behaviors
6
+ # such as close functionality and animations.
7
+ #
8
+ # Example usage:
9
+ # = render Fluxbit::AlertComponent.new(color: :success, icon: "check").with_content("Alert message")
10
+ #
11
+ class Fluxbit::AlertComponent < Fluxbit::Component
12
+ include Fluxbit::Config::AlertComponent
13
+
14
+ ##
15
+ # Initializes the alert component with the given properties.
16
+ #
17
+ # @param [Hash] props The properties to customize the alert.
18
+ # @option props [Symbol, String] :color (:info) The color style of the alert.
19
+ # @option props [Symbol, String, Boolean] :icon (:default) The icon to display in the alert or `false` to omit.
20
+ # @option props [String] :class_icon ('') Additional CSS classes for the icon.
21
+ # @option props [Boolean] :can_close (true) Determines if the alert can be closed.
22
+ # @option props [String] :remove_class ('') Classes to remove from the default class list.
23
+ # @option props [Integer] :dismiss_timeout (3000) The timeout in milliseconds before the alert dismisses itself.
24
+ # @option props [Boolean] :fade_in_animation (true) Determines if the alert should fade in.
25
+ # @option props [Symbol] :out_animation (:fade_out) The type of fade out/dismiss animation.
26
+ # @option props [Boolean] :all_rounded (true) Determines if the alert has rounded corners.
27
+ # @option props [Hash] **props Remaining options declared as HTML attributes.
28
+ #
29
+ # @return [Fluxbit::AlertComponent]
30
+ #
31
+ # @example
32
+ # = render Fluxbit::AlertComponent.new(color: :success, icon: "check").with_content("Alert message")
33
+ #
34
+ def initialize(**props)
35
+ super
36
+ @props = props
37
+ @color = define_color(@props.delete(:color) || @@color)
38
+ @icon = define_icon(@props.delete(:icon) || :default, @props.delete(:class_icon) || "")
39
+ @can_close = @props[:can_close].nil? ? @@can_close : @props.delete(:can_close)
40
+ @all_rounded = @props[:all_rounded].nil? ? @@all_rounded : @props.delete(:all_rounded)
41
+ @props["id"] = fx_id if @props["id"].nil?
42
+ @props[:role] = "alert"
43
+ prop_fade_in_automation = @props.delete(:fade_in_animation)
44
+
45
+ animation_props(
46
+ dismiss_timeout: [ (@props.delete(:dismiss_timeout) || @@dismiss_timeout).to_i, 0 ].max,
47
+ fade_in_animation: prop_fade_in_automation.nil? ? @@fade_in_animation : prop_fade_in_automation,
48
+ out_animation: @props.delete(:out_animation) || @@out_animation
49
+ )
50
+ declare_classes
51
+ @props[:class] = remove_class(@props.delete(:remove_class) || "", @props[:class])
52
+ end
53
+
54
+ def animation_props(dismiss_timeout:, fade_in_animation:, out_animation:)
55
+ return if [ :dont_remove, nil ].include?(out_animation) || dismiss_timeout.to_i == 0
56
+
57
+ @props["data-controller"] = "notification"
58
+ @props["data-action"] = "notification#hide"
59
+ @props["data-notification-delay-value"] = dismiss_timeout.to_s
60
+
61
+ add(to: @props, class: "transition transform duration-1000 hidden")
62
+
63
+ if fade_in_animation
64
+ @props["data-transition-enter-from"] = "opacity-0"
65
+ @props["data-transition-enter-to"] = "opacity-100"
66
+ else
67
+ @props["data-transition-enter-from"] = "opacity-100"
68
+ @props["data-transition-enter-to"] = "opacity-100"
69
+ end
70
+
71
+ @props["data-transition-leave-from"] = styles[:animations][out_animation.to_sym][:from]
72
+ @props["data-transition-leave-to"] = styles[:animations][out_animation.to_sym][:to]
73
+ end
74
+
75
+ def call
76
+ content_tag :div, @props do
77
+ concat @icon
78
+ concat content_tag(:div, content, class: "ml-3 text-sm font-medium")
79
+ concat close_button
80
+ end
81
+ end
82
+
83
+ private
84
+
85
+ def declare_classes
86
+ add to: @props,
87
+ first_element: true,
88
+ class: [
89
+ styles[:colors][@color],
90
+ styles[:all_rounded][@all_rounded ? :on : :off],
91
+ styles[:base]
92
+ ]
93
+ end
94
+
95
+ def define_color(color)
96
+ case color.to_sym
97
+ when :timedout, :alert then :danger
98
+ when *styles[:colors].keys then color
99
+ else @@color # :notice included
100
+ end
101
+ end
102
+
103
+ def define_icon(icon, class_icon = "")
104
+ return "" if icon == false
105
+ icon = icon || @@icon
106
+ return anyicon(icon: icon, class: class_icon) if icon != :default
107
+
108
+ anyicon(icon: "heroicons_solid:#{styles[:default_icons][@color]}", class: class_icon + " w-4 h-4")
109
+ end
110
+
111
+ def close_button
112
+ return "" unless @can_close
113
+
114
+ b_props = {
115
+ "aria-label" => "Close",
116
+ "data-dismiss-target" => "##{@props["id"]}",
117
+ type: "button"
118
+ }
119
+
120
+ add to: b_props, class: [ styles[:close_button][:base], styles[:close_button][:colors][@color] ]
121
+ content_tag :button, b_props do
122
+ concat content_tag(:span, "Dismiss", class: "sr-only")
123
+ concat anyicon(icon: "heroicons_outline:x-mark", class: "w-5 h-5")
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+
4
+ # The `Fluxbit::AvatarComponent` is a component for rendering customizable avatars.
5
+ # It extends `Fluxbit::Component` and provides options for configuring the avatar's
6
+ # appearance and behavior. You can control the avatar's color, placeholder initials,
7
+ # status, size, and other attributes. The avatar can display an image or a placeholder
8
+ # icon if no image is provided.
9
+ class Fluxbit::AvatarComponent < Fluxbit::Component
10
+ include Fluxbit::Config::AvatarComponent
11
+
12
+ # Initializes the avatar component with the given properties.
13
+ #
14
+ # @param [Hash] props The properties to customize the avatar.
15
+ # @option props [String] :color The color of the avatar border.
16
+ # @option props [String] :placeholder_initials The initials to display as a placeholder.
17
+ # @option props [Symbol] :status The status of the avatar (:online, :busy, :offline, :away).
18
+ # @option props [String] :status_position The position of the status indicator.
19
+ # @option props [Boolean] :rounded Whether the avatar should have rounded corners.
20
+ # @option props [Symbol, String] :size The size of the avatar.
21
+ # @option props [String] :src The source URL of the avatar image.
22
+ # @option props [String] :remove_class Classes to be removed from the default avatar class list.
23
+ # @option props [Hash] **props Remaining options declared as HTML attributes, applied to the avatar container.
24
+ def initialize(**props)
25
+ super
26
+ @props = props
27
+ @color = @props.delete(:color) || @@color
28
+ @placeholder_initials = @props.delete(:placeholder_initials) || @@placeholder_initials
29
+ @status = @props[:status].nil? ? @@status : @props.delete(:status) # online, busy, offline, away
30
+ @status_position = (@props.delete(:status_position) || @@status_position).to_s.split("_").map(&:to_sym)
31
+ @rounded = @props[:rounded].nil? ? @@rounded : @props.delete(:rounded)
32
+ @size = @props.delete(:size) || @@size
33
+ @src = @props[:src]
34
+ declare_color
35
+ declare_classes
36
+ @props[:class] = remove_class(@props.delete(:remove_class) || "", @props[:class])
37
+ end
38
+
39
+ def declare_color
40
+ return unless @color.present?
41
+ add to: @props,
42
+ first_element: true,
43
+ class: "#{styles[:bordered]} #{@color.in?(styles[:color].keys) ? styles[:color][@color] : styles[:color][:info]}"
44
+ end
45
+
46
+ def declare_classes
47
+ add to: @props,
48
+ first_element: true,
49
+ class: [
50
+ (!@placeholder_initials && @src.nil? ? styles[:placeholder_icon][:base] : ""),
51
+ @placeholder_initials ? styles[:initials][:base] : "",
52
+ styles[@rounded ? :rounded : :not_rounded][:base],
53
+ (@size.in?(styles[:size].keys) ? styles[:size][@size] : styles[:size][:md])
54
+ ].join(" ")
55
+ end
56
+
57
+ def avatar_itself
58
+ return content_tag(:img, "", @props) unless @src.nil? || @placeholder_initials
59
+
60
+ content_tag :div, @props do
61
+ if @placeholder_initials
62
+ content_tag :span, @placeholder_initials, class: styles[:initials][:text]
63
+ else
64
+ placeholder_icon
65
+ end
66
+ end
67
+ end
68
+
69
+ def placeholder_icon
70
+ svg_attributes = {
71
+ class: [ "absolute", "text-gray-400", "-left-1", placeholder_size ].join(" "),
72
+ fill: "currentColor",
73
+ viewBox: "0 0 20 20",
74
+ xmlns: "http://www.w3.org/2000/svg"
75
+ }
76
+
77
+ path_attributes = {
78
+ "fill-rule": "evenodd",
79
+ d: "M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z",
80
+ "clip-rule": "evenodd"
81
+ }
82
+
83
+ content_tag(:svg, svg_attributes) do
84
+ content_tag(:path, "", path_attributes)
85
+ end
86
+ end
87
+
88
+ def placeholder_size
89
+ return styles[:placeholder_icon][:size][@size] if @size.in?(styles[:placeholder_icon][:size].keys)
90
+
91
+ styles[:size][:md]
92
+ end
93
+
94
+ def dot_indicator
95
+ content_tag :span,
96
+ "",
97
+ class: [
98
+ styles[:status][:base],
99
+ styles[:status][:options][@status],
100
+ styles[@rounded ? :rounded : :not_rounded][:status_position][@status_position[0]],
101
+ styles[@rounded ? :rounded : :not_rounded][:status_position][@status_position[1]][@size]
102
+ ].join(" ")
103
+ end
104
+
105
+ def call
106
+ return avatar_itself unless @status
107
+
108
+ content_tag :div, class: "relative" do
109
+ concat avatar_itself
110
+ concat dot_indicator
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The `Fluxbit::AvatarGroupComponent` is a component for rendering a group of avatars.
4
+ # It extends `Fluxbit::Component` and provides options for configuring the appearance
5
+ # and behavior of the avatar group. You can control the avatars and gravatars displayed
6
+ # within the group. The component supports rendering multiple avatars and gravatars,
7
+ # each of which can be styled or customized through various properties.
8
+ class Fluxbit::AvatarGroupComponent < Fluxbit::Component
9
+ include Fluxbit::Config::AvatarComponent
10
+ renders_many :avatars, Fluxbit::AvatarComponent
11
+ renders_many :gravatars, Fluxbit::GravatarComponent
12
+
13
+ def call
14
+ content_tag :div, class: styles[:group] do
15
+ avatars.each do |avatar|
16
+ concat render(avatar)
17
+ end
18
+ gravatars.each do |gravatar|
19
+ concat render(gravatar)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##
4
+ # The `Fluxbit::BadgeComponent` is a customizable badge component that extends `Fluxbit::Component`.
5
+ # It allows you to create badges with different colors, sizes, borders, and shapes (pill).
6
+ class Fluxbit::BadgeComponent < Fluxbit::Component
7
+ include Fluxbit::Config::BadgeComponent
8
+ renders_one :popover, lambda { |**props, &block|
9
+ @popover_props = props
10
+ @popover_text = block.call
11
+ }
12
+ renders_one :tooltip, lambda { |**props, &block|
13
+ @tooltip_props = props
14
+ @tooltip_text = block.call
15
+ }
16
+
17
+ ##
18
+ # Initializes the badge component with the given properties.
19
+ #
20
+ # @param [Hash] props The properties to customize the badge.
21
+ # @option props [Symbol, String] :color The color style of the badge.
22
+ # @option props [Boolean] :pill (false) Determines if the badge is pill-shaped.
23
+ # @option props [Symbol, Integer] :size The size of the badge (e.g., `0` to `1`).
24
+ # @option props [Symbol, String] :perfect_rounded (:none) Define if the bage is a perfect rounded with size (e.g., `0` to `5`).
25
+ # @option props [Symbol, String] :as The HTML tag to use for the badge (e.g., `:span`, `:div`).
26
+ # @option props [String] :remove_class ('') Classes to remove from the default class list.
27
+ # @option props [String] :popover_text (nil) Popover text (from Fluxbit::Component).
28
+ # @option props [Symbol] :popover_placement (:right) Popover placement (e.g., `:right`, `:left`, `:top`, `:bottom`) (from Fluxbit::Component).
29
+ # @option props [Symbol] :popover_trigger (:hover) Popover trigger (e.g., `:hover`, `:click`) (from Fluxbit::Component).
30
+ # @option props [String] :tooltip_text (nil) Tooltip text (from Fluxbit::Component).
31
+ # @option props [Symbol] :tooltip_placement (:right) Tooltip placement (e.g., `:right`, `:left`, `:top`, `:bottom`) (from Fluxbit::Component).
32
+ # @option props [Symbol] :tooltip_trigger (:hover) Tooltip trigger (e.g., `:hover`, `:click`) (from Fluxbit::Component).
33
+ # @option props [Hash] **props Remaining options declared as HTML attributes.
34
+ #
35
+ # @return [Fluxbit::BadgeComponent]
36
+ def initialize(**props)
37
+ super
38
+ @props = props
39
+ @color = @props.delete(:color) || @@color
40
+ @pill = @props.delete(:pill) || @@pill
41
+ @size = @props.delete(:size) || @@size
42
+ @perfect_rounded = @props.delete(:perfect_rounded) || @@perfect_rounded
43
+ @notification = @props.delete(:notification) || @@notification
44
+ @as = @props.delete(:as) || @@as
45
+ add(class: badge_classes, to: @props, first_element: true)
46
+ @props[:class] = remove_class(@props.delete(:remove_class) || "", @props[:class])
47
+ end
48
+
49
+ def before_render
50
+ add_popover_or_tooltip
51
+ end
52
+
53
+ def call
54
+ concat(content_tag(@as, content, @props) + render_popover_or_tooltip.to_s)
55
+ end
56
+
57
+ private
58
+
59
+ def badge_classes
60
+ @badge_classes ||= begin
61
+ base_classes = [
62
+ styles[:base],
63
+ styles[:colors][@color.to_sym],
64
+ styles[:sizes][@size.to_i],
65
+ styles[:perfect_rounded][@perfect_rounded.to_i],
66
+ notification_classes
67
+ ]
68
+ base_classes << "rounded-full" if @pill
69
+ base_classes.join(" ")
70
+ end
71
+ end
72
+
73
+ def notification_classes
74
+ return "" unless @notification.present?
75
+ ([ styles[:notification][:default] ] + @notification.split(" ").map do |position|
76
+ styles[:notification][:positions][position.to_sym]
77
+ end).join(" ")
78
+ end
79
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##
4
+ # The `Fluxbit::ButtonComponent` is a customizable button component that extends `Fluxbit::Component`.
5
+ # It allows you to create buttons with various styles, sizes, colors, and supports additional
6
+ # features like popovers and tooltips.
7
+ class Fluxbit::ButtonComponent < Fluxbit::Component
8
+ include Fluxbit::Config::ButtonComponent
9
+ renders_one :popover, lambda { |**props, &block|
10
+ @popover_props = props
11
+ @popover_text = block.call
12
+ }
13
+ renders_one :tooltip, lambda { |**props, &block|
14
+ @tooltip_props = props
15
+ @tooltip_text = block.call
16
+ }
17
+
18
+ ##
19
+ # Initializes the button component with the given properties.
20
+ #
21
+ # @param [Hash] props The properties to customize the button.
22
+ # @option props [Object] :form (nil) The form object associated with the button.
23
+ # @option props [Symbol, String] :as The HTML tag to use for the button (e.g., `:button`, `:a`).
24
+ # @option props [Boolean] :pill (false) Determines if the button has pill-shaped edges.
25
+ # @option props [Symbol, String] :color The color style of the button.
26
+ # @option props [Symbol, String] :size The size of the button (e.g., `0` to `4`).
27
+ # @option props [Boolean] :disabled (false) Sets the button to a disabled state.
28
+ # @option props [String] :remove_class ('') Classes to remove from the default class list.
29
+ # @option props [String] :popover_text (nil) Popover text (from Fluxbit::Component).
30
+ # @option props [Symbol] :popover_placement (:right) Popover placement (e.g., `:right`, `:left`, `:top`, `:bottom`) (from Fluxbit::Component).
31
+ # @option props [Symbol] :popover_trigger (:hover) Popover trigger (e.g., `:hover`, `:click`) (from Fluxbit::Component).
32
+ # @option props [String] :tooltip_text (nil) Tooltip text (from Fluxbit::Component).
33
+ # @option props [Symbol] :tooltip_placement (:right) Tooltip placement (e.g., `:right`, `:left`, `:top`, `:bottom`) (from Fluxbit::Component).
34
+ # @option props [Symbol] :tooltip_trigger (:hover) Tooltip trigger (e.g., `:hover`, `:click`) (from Fluxbit::Component).
35
+ # @option props [Hash] **props Remaining options declared as HTML attributes.
36
+ #
37
+ # @return [Fluxbit::ButtonComponent]
38
+ def initialize(**props)
39
+ super
40
+ @props = props
41
+ @form = @props.delete(:form)
42
+ @as = @props.delete(:as) || @@as
43
+ @pill = @props.delete(:pill) || @@pill
44
+ @color = @props.delete(:color) || @@color
45
+ @grouped = @props.delete(:grouped) || false
46
+ @first_button = @props.delete(:first_button) || false
47
+ @last_button = @props.delete(:last_button) || false
48
+ @outline = @color.end_with?("_outline")
49
+ declare_size(@props.delete(:size) || @@size)
50
+ declare_disabled
51
+ declare_classes
52
+ @props[:class] = remove_class(@props.delete(:remove_class) || "", @props[:class])
53
+ end
54
+
55
+ def declare_size(size)
56
+ return if size.blank?
57
+
58
+ add(
59
+ class: (styles[:size][size.to_i]),
60
+ to: @props,
61
+ first_element: true
62
+ )
63
+ end
64
+
65
+ def declare_classes
66
+ add(class: styles[:pill][@pill ? :on : :off], to: @props, first_element: true) unless @outline
67
+ add(class: styles[:outline][:pill][@pill ? :on : :off], to: @props, first_element: true) if @outline
68
+ add(class: styles[:outline][@outline ? :on : :off], to: @props, first_element: true)
69
+ add(class: styles[:base], to: @props, first_element: true)
70
+ add(
71
+ class: (@color.in?(styles[:colors].keys) ? styles[:colors][@color] : styles[:colors][:info]),
72
+ to: @props,
73
+ first_element: true
74
+ )
75
+ add(class: styles[:inner][:base], to: @props) if @grouped
76
+ add(class: styles[:inner][:position][:start], to: @props) if @grouped && @first_button
77
+ add(class: styles[:inner][:position][:end], to: @props) if @grouped && @last_button
78
+ add(class: styles[:inner][:position][:middle], to: @props) if @grouped && !@last_button && !@first_button
79
+ end
80
+
81
+ def declare_disabled
82
+ return unless @props[:disabled].present? && @props[:disabled] == true
83
+
84
+ add(class: styles[:disabled], to: @props, first_element: true)
85
+ end
86
+
87
+ def before_render
88
+ add_popover_or_tooltip
89
+ end
90
+
91
+ def call
92
+ concat(
93
+ (@form.nil? ? content_tag(@as, content, @props) : @form.submit(**@props)) +
94
+ render_popover_or_tooltip.to_s
95
+ )
96
+ end
97
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The `Fluxbit::ButtonGroupComponent` is a component for rendering a group of buttons.
4
+ # It extends `Fluxbit::Component` and provides options for configuring the appearance
5
+ # and behavior of the button group. You can control the buttons displayed
6
+ # within the group. The component supports rendering multiple buttons,
7
+ # each of which can be styled or customized through various properties.
8
+ class Fluxbit::ButtonGroupComponent < Fluxbit::Component
9
+ attr_accessor :buttons_group
10
+ include Fluxbit::Config::ButtonComponent
11
+ # renders_many :buttons, Fluxbit::ButtonComponent
12
+
13
+ renders_many :buttons, lambda { |**props, &block|
14
+ @buttons_group << ComponentObj.new(props, view_context.capture(&block))
15
+ }
16
+
17
+ # Initializes the button group component with the given properties.
18
+ #
19
+ # @param [Hash] props The properties to customize the button group.
20
+ # @option props [Hash] **props Remaining options declared as HTML attributes, applied to the button group container.
21
+ def initialize(**props)
22
+ super
23
+ @props = props
24
+ @buttons_group = []
25
+ add class: styles[:group], to: @props
26
+ end
27
+
28
+ def call
29
+ buttons
30
+ content_tag :div, **@props do
31
+ @buttons_group.each_with_index do |button, index|
32
+ button_props = button.props || {}
33
+ button_content = button.content
34
+
35
+ button_props[:grouped] = true
36
+ button_props[:first_button] = true if index == 0
37
+ button_props[:last_button] = true if index == @buttons_group.size - 1
38
+
39
+ concat Fluxbit::ButtonComponent.new(**button_props).with_content(button_content).render_in(view_context)
40
+ end
41
+ end
42
+ end
43
+ end