rails-active-ui 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 (167) hide show
  1. checksums.yaml +7 -0
  2. data/Rakefile +6 -0
  3. data/app/assets/stylesheets.css +73555 -0
  4. data/app/components/accordion_component.rb +34 -0
  5. data/app/components/ad_component.rb +28 -0
  6. data/app/components/api_component.rb +24 -0
  7. data/app/components/breadcrumb_component.rb +26 -0
  8. data/app/components/button_component.rb +49 -0
  9. data/app/components/calendar_component.rb +34 -0
  10. data/app/components/card_component.rb +56 -0
  11. data/app/components/checkbox_component.rb +41 -0
  12. data/app/components/column_component.rb +62 -0
  13. data/app/components/comment_component.rb +45 -0
  14. data/app/components/concerns/alignable.rb +21 -0
  15. data/app/components/concerns/attachable.rb +16 -0
  16. data/app/components/concerns/orientable.rb +21 -0
  17. data/app/components/concerns/positionable.rb +21 -0
  18. data/app/components/concerns/sizeable.rb +18 -0
  19. data/app/components/container_component.rb +23 -0
  20. data/app/components/dimmer_component.rb +30 -0
  21. data/app/components/divider_component.rb +30 -0
  22. data/app/components/dropdown_component.rb +63 -0
  23. data/app/components/embed_component.rb +32 -0
  24. data/app/components/emoji_component.rb +15 -0
  25. data/app/components/feed_component.rb +22 -0
  26. data/app/components/flag_component.rb +15 -0
  27. data/app/components/flyout_component.rb +41 -0
  28. data/app/components/form_component.rb +39 -0
  29. data/app/components/grid_component.rb +85 -0
  30. data/app/components/h_stack_component.rb +67 -0
  31. data/app/components/header_component.rb +60 -0
  32. data/app/components/icon_component.rb +41 -0
  33. data/app/components/image_component.rb +46 -0
  34. data/app/components/input_component.rb +52 -0
  35. data/app/components/item_component.rb +39 -0
  36. data/app/components/item_group_component.rb +30 -0
  37. data/app/components/label_component.rb +49 -0
  38. data/app/components/link_component.rb +23 -0
  39. data/app/components/list_component.rb +39 -0
  40. data/app/components/loader_component.rb +33 -0
  41. data/app/components/menu_component.rb +64 -0
  42. data/app/components/menu_item_component.rb +52 -0
  43. data/app/components/message_component.rb +54 -0
  44. data/app/components/modal_component.rb +50 -0
  45. data/app/components/nag_component.rb +25 -0
  46. data/app/components/overlay_component.rb +16 -0
  47. data/app/components/placeholder_component.rb +39 -0
  48. data/app/components/popup_component.rb +31 -0
  49. data/app/components/progress_component.rb +48 -0
  50. data/app/components/pusher_component.rb +18 -0
  51. data/app/components/rail_component.rb +31 -0
  52. data/app/components/rating_component.rb +41 -0
  53. data/app/components/reset_component.rb +12 -0
  54. data/app/components/reveal_component.rb +39 -0
  55. data/app/components/row_component.rb +39 -0
  56. data/app/components/search_component.rb +44 -0
  57. data/app/components/segment_component.rb +57 -0
  58. data/app/components/segment_group_component.rb +36 -0
  59. data/app/components/shape_component.rb +25 -0
  60. data/app/components/sidebar_component.rb +33 -0
  61. data/app/components/site_component.rb +12 -0
  62. data/app/components/slider_component.rb +46 -0
  63. data/app/components/state_component.rb +25 -0
  64. data/app/components/statistic_component.rb +43 -0
  65. data/app/components/step_component.rb +56 -0
  66. data/app/components/step_group_component.rb +38 -0
  67. data/app/components/sticky_component.rb +22 -0
  68. data/app/components/sub_header_component.rb +15 -0
  69. data/app/components/sub_menu_component.rb +24 -0
  70. data/app/components/tab_component.rb +24 -0
  71. data/app/components/table_cell_component.rb +60 -0
  72. data/app/components/table_component.rb +160 -0
  73. data/app/components/table_row_component.rb +43 -0
  74. data/app/components/text_component.rb +73 -0
  75. data/app/components/toast_component.rb +36 -0
  76. data/app/components/transition_component.rb +32 -0
  77. data/app/components/v_stack_component.rb +31 -0
  78. data/app/components/visibility_component.rb +22 -0
  79. data/app/helpers/component_helper.rb +109 -0
  80. data/app/helpers/fui_helper.rb +53 -0
  81. data/app/javascript/accordion.js +547 -0
  82. data/app/javascript/accordion.min.js +11 -0
  83. data/app/javascript/api.js +1112 -0
  84. data/app/javascript/api.min.js +11 -0
  85. data/app/javascript/calendar.js +1960 -0
  86. data/app/javascript/calendar.min.js +11 -0
  87. data/app/javascript/checkbox.js +819 -0
  88. data/app/javascript/checkbox.min.js +11 -0
  89. data/app/javascript/dimmer.js +686 -0
  90. data/app/javascript/dimmer.min.js +11 -0
  91. data/app/javascript/dropdown.js +4019 -0
  92. data/app/javascript/dropdown.min.js +11 -0
  93. data/app/javascript/embed.js +646 -0
  94. data/app/javascript/embed.min.js +11 -0
  95. data/app/javascript/flyout.js +1405 -0
  96. data/app/javascript/flyout.min.js +11 -0
  97. data/app/javascript/form.js +2070 -0
  98. data/app/javascript/form.min.js +11 -0
  99. data/app/javascript/jquery.js +10716 -0
  100. data/app/javascript/jquery.min.js +2 -0
  101. data/app/javascript/modal.js +1507 -0
  102. data/app/javascript/modal.min.js +11 -0
  103. data/app/javascript/nag.js +522 -0
  104. data/app/javascript/nag.min.js +11 -0
  105. data/app/javascript/popup.js +1457 -0
  106. data/app/javascript/popup.min.js +11 -0
  107. data/app/javascript/progress.js +922 -0
  108. data/app/javascript/progress.min.js +11 -0
  109. data/app/javascript/rating.js +496 -0
  110. data/app/javascript/rating.min.js +11 -0
  111. data/app/javascript/search.js +1519 -0
  112. data/app/javascript/search.min.js +11 -0
  113. data/app/javascript/shape.js +721 -0
  114. data/app/javascript/shape.min.js +11 -0
  115. data/app/javascript/sidebar.js +952 -0
  116. data/app/javascript/sidebar.min.js +11 -0
  117. data/app/javascript/site.js +415 -0
  118. data/app/javascript/site.min.js +11 -0
  119. data/app/javascript/slider.js +1449 -0
  120. data/app/javascript/slider.min.js +11 -0
  121. data/app/javascript/state.js +653 -0
  122. data/app/javascript/state.min.js +11 -0
  123. data/app/javascript/sticky.js +852 -0
  124. data/app/javascript/sticky.min.js +11 -0
  125. data/app/javascript/tab.js +867 -0
  126. data/app/javascript/tab.min.js +11 -0
  127. data/app/javascript/toast.js +916 -0
  128. data/app/javascript/toast.min.js +11 -0
  129. data/app/javascript/transition.js +955 -0
  130. data/app/javascript/transition.min.js +11 -0
  131. data/app/javascript/ui/controllers/fui_accordion_controller.js +45 -0
  132. data/app/javascript/ui/controllers/fui_api_controller.js +80 -0
  133. data/app/javascript/ui/controllers/fui_calendar_controller.js +66 -0
  134. data/app/javascript/ui/controllers/fui_checkbox_controller.js +48 -0
  135. data/app/javascript/ui/controllers/fui_dimmer_controller.js +45 -0
  136. data/app/javascript/ui/controllers/fui_dropdown_controller.js +68 -0
  137. data/app/javascript/ui/controllers/fui_embed_controller.js +49 -0
  138. data/app/javascript/ui/controllers/fui_flyout_controller.js +49 -0
  139. data/app/javascript/ui/controllers/fui_form_controller.js +62 -0
  140. data/app/javascript/ui/controllers/fui_modal_controller.js +61 -0
  141. data/app/javascript/ui/controllers/fui_nag_controller.js +52 -0
  142. data/app/javascript/ui/controllers/fui_popup_controller.js +58 -0
  143. data/app/javascript/ui/controllers/fui_progress_controller.js +60 -0
  144. data/app/javascript/ui/controllers/fui_rating_controller.js +49 -0
  145. data/app/javascript/ui/controllers/fui_search_controller.js +76 -0
  146. data/app/javascript/ui/controllers/fui_shape_controller.js +45 -0
  147. data/app/javascript/ui/controllers/fui_sidebar_controller.js +48 -0
  148. data/app/javascript/ui/controllers/fui_site_controller.js +29 -0
  149. data/app/javascript/ui/controllers/fui_slider_controller.js +53 -0
  150. data/app/javascript/ui/controllers/fui_state_controller.js +63 -0
  151. data/app/javascript/ui/controllers/fui_sticky_controller.js +50 -0
  152. data/app/javascript/ui/controllers/fui_tab_controller.js +57 -0
  153. data/app/javascript/ui/controllers/fui_toast_controller.js +60 -0
  154. data/app/javascript/ui/controllers/fui_transition_controller.js +60 -0
  155. data/app/javascript/ui/controllers/fui_visibility_controller.js +55 -0
  156. data/app/javascript/ui/index.js +114 -0
  157. data/app/javascript/visibility.js +1196 -0
  158. data/app/javascript/visibility.min.js +11 -0
  159. data/app/lib/component.rb +63 -0
  160. data/config/importmap.rb +27 -0
  161. data/config/initializers/ruby_template_handler.rb +31 -0
  162. data/config/routes.rb +2 -0
  163. data/lib/tasks/ui_tasks.rake +4 -0
  164. data/lib/ui/engine.rb +27 -0
  165. data/lib/ui/version.rb +3 -0
  166. data/lib/ui.rb +6 -0
  167. metadata +220 -0
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ # List — ordered, unordered, and bulleted lists.
4
+ #
5
+ # Usage:
6
+ # List(bulleted: true) { text "items..." }
7
+ # List(divided: true, relaxed: true) { text "items..." }
8
+
9
+ class ListComponent < Component
10
+ attribute :ordered, :boolean, default: false
11
+ attribute :bulleted, :boolean, default: false
12
+ attribute :divided, :boolean, default: false
13
+ attribute :relaxed, :boolean, default: false
14
+ attribute :animated, :boolean, default: false
15
+ attribute :horizontal, :boolean, default: false
16
+ attribute :inverted, :boolean, default: false
17
+ attribute :size, :string, default: nil
18
+ attribute :celled, :boolean, default: false
19
+ attribute :selection, :boolean, default: false
20
+ attribute :link, :boolean, default: false
21
+
22
+ def to_s
23
+ classes = class_names(
24
+ "ui",
25
+ size,
26
+ { "ordered" => ordered, "bulleted" => bulleted, "divided" => divided,
27
+ "relaxed" => relaxed, "animated" => animated, "horizontal" => horizontal,
28
+ "inverted" => inverted, "celled" => celled, "selection" => selection,
29
+ "link" => link },
30
+ "list"
31
+ )
32
+
33
+ if ordered
34
+ tag.ol(class: classes) { @content }
35
+ else
36
+ tag.div(class: classes) { @content }
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Loader — loading spinners.
4
+ #
5
+ # Usage:
6
+ # Loader(active: true)
7
+ # Loader(active: true, size: :large) { text "Loading..." }
8
+ # Loader(inline: true, centered: true, active: true)
9
+
10
+ class LoaderComponent < Component
11
+ attribute :active, :boolean, default: false
12
+ attribute :inline, :boolean, default: false
13
+ attribute :centered, :boolean, default: false
14
+ attribute :size, :string, default: nil
15
+ attribute :inverted, :boolean, default: false
16
+ attribute :indeterminate, :boolean, default: false
17
+ attribute :color, :string, default: nil
18
+ attribute :disabled, :boolean, default: false
19
+
20
+ def to_s
21
+ classes = class_names(
22
+ "ui",
23
+ size,
24
+ color,
25
+ { "active" => active, "disabled" => disabled, "inline" => inline,
26
+ "centered" => centered, "inverted" => inverted,
27
+ "indeterminate" => indeterminate, "text" => @content.present? },
28
+ "loader"
29
+ )
30
+
31
+ tag.div(class: classes) { @content }
32
+ end
33
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Menu — navigation menus.
4
+ #
5
+ # Usage:
6
+ # Menu(pointing: true, secondary: true) {
7
+ # MenuItem(href: "#", active: true) { text "Home" }
8
+ # MenuItem(href: "#") { text "Messages" }
9
+ # }
10
+
11
+ class MenuComponent < Component
12
+ include Positionable
13
+ include Attachable
14
+ include Sizeable
15
+
16
+ attribute :secondary, :boolean, default: false
17
+ attribute :pointing, :boolean, default: false
18
+ attribute :tabular, :boolean, default: false
19
+ attribute :vertical, :boolean, default: false
20
+ attribute :fixed, :string, default: nil
21
+ attribute :inverted, :boolean, default: false
22
+ attribute :icon, :boolean, default: false
23
+ attribute :labeled_icon, :boolean, default: false
24
+ attribute :compact, :boolean, default: false
25
+ attribute :fluid, :boolean, default: false
26
+ attribute :color, :string, default: nil
27
+ attribute :stackable, :boolean, default: false
28
+ attribute :text, :boolean, default: false
29
+ attribute :pagination, :boolean, default: false
30
+ attribute :borderless, :boolean, default: false
31
+ attribute :items, :integer, default: nil
32
+
33
+ NUMBERS = %w[one two three four five six seven eight nine ten eleven twelve].freeze
34
+
35
+ def to_s
36
+ item_word = items && items.between?(1, 12) ? NUMBERS[items - 1] : nil
37
+
38
+ classes = class_names(
39
+ "ui",
40
+ position,
41
+ size,
42
+ color,
43
+ item_word && "#{item_word} item",
44
+ fixed && "#{fixed} fixed",
45
+ { "attached" => attached,
46
+ "secondary" => secondary,
47
+ "pointing" => pointing,
48
+ "tabular" => tabular,
49
+ "vertical" => vertical,
50
+ "inverted" => inverted,
51
+ "icon" => icon && !labeled_icon,
52
+ "labeled icon" => labeled_icon,
53
+ "compact" => compact,
54
+ "fluid" => fluid,
55
+ "stackable" => stackable,
56
+ "text" => text,
57
+ "pagination" => pagination,
58
+ "borderless" => borderless },
59
+ "menu"
60
+ )
61
+
62
+ tag.div(class: classes) { @content }
63
+ end
64
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ # MenuItem — individual item within a Menu or Sidebar.
4
+ #
5
+ # Usage:
6
+ # Menu {
7
+ # MenuItem(href: "/") { text "Home" }
8
+ # MenuItem(active: true) { text "Current" }
9
+ # MenuItem(header: true) { text "Section Header" }
10
+ # MenuItem(icon: "settings") { text "Settings" }
11
+ # }
12
+
13
+ class MenuItemComponent < Component
14
+ include Positionable
15
+
16
+ attribute :href, :string, default: nil
17
+ attribute :active, :boolean, default: false
18
+ attribute :disabled, :boolean, default: false
19
+ attribute :header, :boolean, default: false
20
+ attribute :icon, :string, default: nil
21
+ attribute :fitted, :string, default: nil
22
+ attribute :link, :boolean, default: false
23
+ attribute :color, :string, default: nil
24
+ attribute :dropdown, :boolean, default: false
25
+ attribute :value, :string, default: nil
26
+
27
+ def to_s
28
+ classes = class_names(
29
+ color,
30
+ fitted && (fitted == "true" ? "fitted" : "#{fitted} fitted"),
31
+ position,
32
+ { "active" => active,
33
+ "disabled" => disabled,
34
+ "header" => header,
35
+ "link" => link,
36
+ "dropdown" => dropdown },
37
+ "item"
38
+ )
39
+
40
+ icon_el = icon ? tag.i(class: "#{icon} icon") : nil
41
+ inner = icon_el ? safe_join([ icon_el, @content ]) : @content
42
+
43
+ opts = { class: classes }
44
+ opts["data-value"] = value if value
45
+
46
+ if href
47
+ tag.a(**opts, href: href) { inner }
48
+ else
49
+ tag.div(**opts) { inner }
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Message — alert/info messages.
4
+ #
5
+ # Usage:
6
+ # Message(type: :success) { text "It worked!" }
7
+ # Message(type: :error, icon: "exclamation triangle") { |c|
8
+ # c.header { text "Error" }
9
+ # text "Something went wrong."
10
+ # }
11
+ # Message(dismissible: true) { text "Notice" }
12
+
13
+ class MessageComponent < Component
14
+ include Attachable
15
+
16
+ attribute :type, :string, default: nil
17
+ attribute :icon, :string, default: nil
18
+ attribute :dismissible, :boolean, default: false
19
+ attribute :floating, :boolean, default: false
20
+ attribute :compact, :boolean, default: false
21
+ attribute :color, :string, default: nil
22
+ attribute :size, :string, default: nil
23
+ attribute :hidden, :boolean, default: false
24
+ attribute :visible, :boolean, default: false
25
+
26
+ slot :header
27
+
28
+ def to_s
29
+ classes = class_names(
30
+ "ui",
31
+ size,
32
+ color,
33
+ type,
34
+ { "attached" => attached,
35
+ "icon" => icon,
36
+ "floating" => floating,
37
+ "compact" => compact,
38
+ "hidden" => hidden,
39
+ "visible" => visible },
40
+ "message"
41
+ )
42
+
43
+ close_el = dismissible ? tag.i(class: "close icon") : nil
44
+ icon_el = icon ? tag.i(class: "#{icon} icon") : nil
45
+ header_el = @slots[:header] ? tag.div(class: "header") { @slots[:header] } : nil
46
+
47
+ content_parts = safe_join([ header_el, @content.presence ])
48
+ content_wrapper = icon ? tag.div(class: "content") { content_parts } : content_parts
49
+
50
+ tag.div(class: classes) {
51
+ safe_join([ close_el, icon_el, content_wrapper ])
52
+ }
53
+ end
54
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Modal — dialog windows with dimmer.
4
+ #
5
+ # Usage:
6
+ # Modal(size: :small) { |c|
7
+ # c.header { text "Confirm Action" }
8
+ # c.content { text "Are you sure?" }
9
+ # c.actions {
10
+ # Button(variant: :deny) { text "Cancel" }
11
+ # Button(variant: :approve, color: :green) { text "OK" }
12
+ # }
13
+ # }
14
+
15
+ class ModalComponent < Component
16
+ attribute :size, :string, default: nil
17
+ attribute :basic, :boolean, default: false
18
+ attribute :closable, :boolean, default: true
19
+ attribute :blurring, :boolean, default: false
20
+ attribute :longer, :boolean, default: false
21
+ attribute :fullscreen, :boolean, default: false
22
+
23
+ slot :header
24
+ slot :content
25
+ slot :actions
26
+
27
+ def to_s
28
+ classes = class_names(
29
+ "ui",
30
+ size,
31
+ { "basic" => basic,
32
+ "longer" => longer,
33
+ "fullscreen" => fullscreen },
34
+ "modal"
35
+ )
36
+
37
+ data = { controller: "fui-modal" }
38
+ data[:fui_modal_closable_value] = "false" unless closable
39
+ data[:fui_modal_blurring_value] = "true" if blurring
40
+
41
+ close_el = closable ? tag.i(class: "close icon") : nil
42
+ header_el = @slots[:header] ? tag.div(class: "header") { @slots[:header] } : nil
43
+ content_el = @slots[:content] ? tag.div(class: "content") { @slots[:content] } : nil
44
+ actions_el = @slots[:actions] ? tag.div(class: "actions") { @slots[:actions] } : nil
45
+
46
+ tag.div(class: classes, data: data) {
47
+ safe_join([ close_el, header_el, content_el, @content.presence, actions_el ])
48
+ }
49
+ end
50
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Nag — persistent notification/cookie nag bar.
4
+ #
5
+ # Usage:
6
+ # Nag { text "We use cookies." }
7
+ # Nag(fixed: true) { text "Update available!" }
8
+
9
+ class NagComponent < Component
10
+ attribute :fixed, :boolean, default: false
11
+
12
+ def to_s
13
+ classes = class_names(
14
+ "ui",
15
+ { "fixed" => fixed },
16
+ "nag"
17
+ )
18
+
19
+ close_el = tag.i(class: "close icon")
20
+
21
+ tag.div(class: classes, data: { controller: "fui-nag" }) {
22
+ safe_join([ close_el, @content ])
23
+ }
24
+ end
25
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Overlay — floating overlay wrapper for positioned content.
4
+ #
5
+ # Usage:
6
+ # Overlay {
7
+ # Menu(vertical: true, labeled_icon: true) {
8
+ # MenuItem(href: "#", icon: "twitter") { text "Tweet" }
9
+ # }
10
+ # }
11
+
12
+ class OverlayComponent < Component
13
+ def to_s
14
+ tag.div(class: "overlay") { @content }
15
+ end
16
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Placeholder — skeleton/placeholder content while loading.
4
+ #
5
+ # Usage:
6
+ # Placeholder { |c|
7
+ # c.header_slot {
8
+ # text '<div class="line"></div><div class="line"></div>'.html_safe
9
+ # }
10
+ # c.paragraph {
11
+ # text '<div class="line"></div><div class="line"></div>'.html_safe
12
+ # }
13
+ # }
14
+
15
+ class PlaceholderComponent < Component
16
+ attribute :fluid, :boolean, default: false
17
+ attribute :inverted, :boolean, default: false
18
+
19
+ slot :header_slot
20
+ slot :paragraph
21
+ slot :image_slot
22
+
23
+ def to_s
24
+ classes = class_names(
25
+ "ui",
26
+ { "fluid" => fluid,
27
+ "inverted" => inverted },
28
+ "placeholder"
29
+ )
30
+
31
+ header_el = @slots[:header_slot] ? tag.div(class: "header") { @slots[:header_slot] } : nil
32
+ para_el = @slots[:paragraph] ? tag.div(class: "paragraph") { @slots[:paragraph] } : nil
33
+ image_el = @slots[:image_slot] ? tag.div(class: "image") { @slots[:image_slot] } : nil
34
+
35
+ tag.div(class: classes) {
36
+ safe_join([ header_el, para_el, image_el, @content.presence ])
37
+ }
38
+ end
39
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Popup — tooltips and popup content.
4
+ #
5
+ # Usage:
6
+ # Popup(content: "Tooltip text", position: "top center") {
7
+ # Button { text "Hover me" }
8
+ # }
9
+ # Popup(hoverable: true) { text "Hover for popup" }
10
+
11
+ class PopupComponent < Component
12
+ include Positionable
13
+ default position: "top center"
14
+
15
+ attribute :content, :string, default: nil
16
+ attribute :title, :string, default: nil
17
+ attribute :variation, :string, default: nil
18
+ attribute :hoverable, :boolean, default: false
19
+ attribute :on, :string, default: "hover"
20
+
21
+ def to_s
22
+ data = { controller: "fui-popup", fui_popup_position_value: position }
23
+ data[:fui_popup_content_value] = content if content
24
+ data[:fui_popup_title_value] = title if title
25
+ data[:fui_popup_hoverable_value] = "true" if hoverable
26
+ data[:fui_popup_variation_value] = variation if variation
27
+ data[:fui_popup_on_value] = on if on != "hover"
28
+
29
+ tag.span(data: data) { @content }
30
+ end
31
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Progress — progress bars.
4
+ #
5
+ # Usage:
6
+ # Progress(value: 65, total: 100, color: :green, active: true) { |c|
7
+ # c.bar { }
8
+ # c.label { text "65% Complete" }
9
+ # }
10
+ # Progress(value: 3, total: 10, indicating: true)
11
+
12
+ class ProgressComponent < Component
13
+ attribute :value, :integer, default: 0
14
+ attribute :total, :integer, default: 100
15
+ attribute :indicating, :boolean, default: false
16
+ attribute :active, :boolean, default: false
17
+ attribute :color, :string, default: nil
18
+ attribute :size, :string, default: nil
19
+ attribute :attached, :string, default: nil
20
+ attribute :inverted, :boolean, default: false
21
+ attribute :disabled, :boolean, default: false
22
+
23
+ slot :bar
24
+ slot :label
25
+
26
+ def to_s
27
+ percent = total > 0 ? ((value.to_f / total) * 100).round : 0
28
+
29
+ classes = class_names(
30
+ "ui",
31
+ color,
32
+ size,
33
+ attached && "#{attached} attached",
34
+ { "indicating" => indicating,
35
+ "active" => active,
36
+ "inverted" => inverted,
37
+ "disabled" => disabled },
38
+ "progress"
39
+ )
40
+
41
+ bar_el = tag.div(class: "bar", style: "width:#{percent}%") { @slots[:bar] }
42
+ label_el = @slots[:label] ? tag.div(class: "label") { @slots[:label] } : nil
43
+
44
+ tag.div(class: classes, data: { controller: "fui-progress", percent: percent }) {
45
+ safe_join([ bar_el, label_el, @content.presence ])
46
+ }
47
+ end
48
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Pusher — main content wrapper for Sidebar layouts.
4
+ # The pusher wraps the page content that gets pushed when a sidebar opens.
5
+ #
6
+ # Usage:
7
+ # Sidebar(inverted: true) {
8
+ # MenuItem(href: "/") { text "Home" }
9
+ # }
10
+ # Pusher {
11
+ # text "Main content here"
12
+ # }
13
+
14
+ class PusherComponent < Component
15
+ def to_s
16
+ tag.div(class: "pusher") { @content }
17
+ end
18
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Rail — content rails floating alongside main content.
4
+ #
5
+ # Usage:
6
+ # Rail(position: :left) { text "Side content" }
7
+ # Rail(position: :right, close: true) { text "Close rail" }
8
+
9
+ class RailComponent < Component
10
+ include Positionable
11
+ default position: "left"
12
+
13
+ attribute :close, :boolean, default: false
14
+ attribute :attached, :boolean, default: false
15
+ attribute :dividing, :boolean, default: false
16
+ attribute :internal, :boolean, default: false
17
+
18
+ def to_s
19
+ classes = class_names(
20
+ "ui",
21
+ position,
22
+ { "close" => close,
23
+ "attached" => attached,
24
+ "dividing" => dividing,
25
+ "internal" => internal },
26
+ "rail"
27
+ )
28
+
29
+ tag.div(class: classes) { @content }
30
+ end
31
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Rating — star/heart rating widget.
4
+ #
5
+ # Usage:
6
+ # Rating(max_rating: 5, rating: 3)
7
+ # Rating(max_rating: 5, rating: 4, icon: :heart, size: :large)
8
+ # Rating(max_rating: 10, disabled: true, rating: 7)
9
+
10
+ class RatingComponent < Component
11
+ attribute :max_rating, :integer, default: 5
12
+ attribute :rating, :integer, default: 0
13
+ attribute :icon, :string, default: "star"
14
+ attribute :size, :string, default: nil
15
+ attribute :disabled, :boolean, default: false
16
+ attribute :clearable, :boolean, default: false
17
+ attribute :name, :string, default: nil
18
+
19
+ def to_s
20
+ classes = class_names(
21
+ "ui",
22
+ icon,
23
+ size,
24
+ { "disabled" => disabled },
25
+ "rating"
26
+ )
27
+
28
+ data = {
29
+ controller: "fui-rating",
30
+ fui_rating_max_rating_value: max_rating,
31
+ fui_rating_initial_rating_value: rating
32
+ }
33
+ data[:fui_rating_clearable_value] = "true" if clearable
34
+
35
+ name_el = name ? tag.input(type: "hidden", name: name, value: rating) : nil
36
+
37
+ tag.div(class: classes, data: data) {
38
+ safe_join([ name_el, @content ])
39
+ }
40
+ end
41
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Reset — CSS reset/normalize.
4
+ #
5
+ # Fomantic-UI's reset is loaded via the asset pipeline (semantic.css).
6
+ # This component is a no-op marker for inventory completeness.
7
+
8
+ class ResetComponent < Component
9
+ def to_s
10
+ tag.comment("fomantic-ui reset loaded via asset pipeline")
11
+ end
12
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Reveal — hidden content revealed on hover/active.
4
+ #
5
+ # Usage:
6
+ # Reveal(type: :fade) { |c|
7
+ # c.visible { Image(src: "before.jpg") }
8
+ # c.hidden { Image(src: "after.jpg") }
9
+ # }
10
+
11
+ class RevealComponent < Component
12
+ attribute :type, :string, default: "fade"
13
+ attribute :active, :boolean, default: false
14
+ attribute :instant, :boolean, default: false
15
+ attribute :disabled, :boolean, default: false
16
+ attribute :move_direction, :string, default: nil
17
+
18
+ slot :visible
19
+ slot :hidden
20
+
21
+ def to_s
22
+ classes = class_names(
23
+ "ui",
24
+ type,
25
+ (type == "move") && move_direction,
26
+ { "active" => active,
27
+ "instant" => instant,
28
+ "disabled" => disabled },
29
+ "reveal"
30
+ )
31
+
32
+ visible_el = @slots[:visible] ? tag.div(class: "visible content") { @slots[:visible] } : nil
33
+ hidden_el = @slots[:hidden] ? tag.div(class: "hidden content") { @slots[:hidden] } : nil
34
+
35
+ tag.div(class: classes) {
36
+ safe_join([ visible_el, hidden_el, @content ])
37
+ }
38
+ end
39
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Row — grid row with optional column count.
4
+ #
5
+ # Usage:
6
+ # Grid {
7
+ # Row(columns: 3) {
8
+ # Column { text "A" }
9
+ # Column { text "B" }
10
+ # Column { text "C" }
11
+ # }
12
+ # }
13
+
14
+ class RowComponent < Component
15
+ NUMBERS = %w[one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen sixteen].freeze
16
+
17
+ attribute :columns, :integer, default: nil
18
+ attribute :centered, :boolean, default: false
19
+ attribute :stretched, :boolean, default: false
20
+ attribute :equal_width, :boolean, default: false
21
+ attribute :only, :string, default: nil
22
+ attribute :aligned, :string, default: nil
23
+
24
+ def to_s
25
+ col_word = columns && columns.between?(1, 16) ? NUMBERS[columns - 1] : nil
26
+
27
+ classes = class_names(
28
+ col_word && "#{col_word} column",
29
+ only && "#{only} only",
30
+ aligned && "#{aligned} aligned",
31
+ { "centered" => centered,
32
+ "stretched" => stretched,
33
+ "equal width" => equal_width },
34
+ "row"
35
+ )
36
+
37
+ tag.div(class: classes) { @content }
38
+ end
39
+ end