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,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Search — search component with local/remote data.
4
+ #
5
+ # Usage:
6
+ # Search(placeholder: "Search...", name: "q")
7
+ # Search(category: true, fluid: true, size: :large)
8
+
9
+ class SearchComponent < Component
10
+ attribute :category, :boolean, default: false
11
+ attribute :placeholder, :string, default: "Search..."
12
+ attribute :min_characters, :integer, default: 1
13
+ attribute :fluid, :boolean, default: false
14
+ attribute :size, :string, default: nil
15
+ attribute :name, :string, default: nil
16
+ attribute :url, :string, default: nil
17
+ attribute :aligned, :string, default: nil
18
+
19
+ def to_s
20
+ classes = class_names(
21
+ "ui",
22
+ size,
23
+ aligned && "#{aligned} aligned",
24
+ { "category" => category,
25
+ "fluid" => fluid },
26
+ "search"
27
+ )
28
+
29
+ data = { controller: "fui-search" }
30
+ data[:fui_search_url_value] = url if url
31
+
32
+ input_opts = { class: "prompt", type: "text", placeholder: placeholder }
33
+ input_opts[:name] = name if name
34
+
35
+ input_wrapper = tag.div(class: "ui icon input") {
36
+ safe_join([ tag.input(**input_opts), tag.i(class: "search icon") ])
37
+ }
38
+ results_el = tag.div(class: "results")
39
+
40
+ tag.div(class: classes, data: data) {
41
+ safe_join([ input_wrapper, results_el, @content ])
42
+ }
43
+ end
44
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Segment — grouped content sections.
4
+ #
5
+ # Usage:
6
+ # Segment { text "Basic segment" }
7
+ # Segment(raised: true, padded: :very) { text "Raised" }
8
+ # Segment(inverted: true, color: :blue) { text "Blue inverted" }
9
+
10
+ class SegmentComponent < Component
11
+ include Attachable
12
+ include Sizeable
13
+
14
+ attribute :raised, :boolean, default: false
15
+ attribute :stacked, :boolean, default: false
16
+ attribute :piled, :boolean, default: false
17
+ attribute :vertical, :boolean, default: false
18
+ attribute :inverted, :boolean, default: false
19
+ attribute :padded, :string, default: nil
20
+ attribute :compact, :boolean, default: false
21
+ attribute :color, :string, default: nil
22
+ attribute :loading, :boolean, default: false
23
+ attribute :clearing, :boolean, default: false
24
+ attribute :basic, :boolean, default: false
25
+ attribute :circular, :boolean, default: false
26
+ attribute :disabled, :boolean, default: false
27
+ attribute :placeholder_seg, :boolean, default: false
28
+ attribute :secondary, :boolean, default: false
29
+ attribute :tertiary, :boolean, default: false
30
+
31
+ def to_s
32
+ classes = class_names(
33
+ "ui",
34
+ size,
35
+ color,
36
+ padded && "#{padded == "very" ? "very " : ""}padded",
37
+ { "attached" => attached,
38
+ "raised" => raised,
39
+ "stacked" => stacked,
40
+ "piled" => piled,
41
+ "vertical" => vertical,
42
+ "inverted" => inverted,
43
+ "compact" => compact,
44
+ "loading" => loading,
45
+ "clearing" => clearing,
46
+ "basic" => basic,
47
+ "circular" => circular,
48
+ "disabled" => disabled,
49
+ "placeholder" => placeholder_seg,
50
+ "secondary" => secondary,
51
+ "tertiary" => tertiary },
52
+ "segment"
53
+ )
54
+
55
+ tag.div(class: classes) { @content }
56
+ end
57
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SegmentGroup — wraps multiple segments into a visual group.
4
+ #
5
+ # Usage:
6
+ # SegmentGroup {
7
+ # Segment { text "Top" }
8
+ # Segment { text "Bottom" }
9
+ # }
10
+ #
11
+ # SegmentGroup(aligned: "horizontal") {
12
+ # Segment { text "Left" }
13
+ # Segment { text "Right" }
14
+ # }
15
+
16
+ class SegmentGroupComponent < Component
17
+ include Alignable
18
+
19
+ attribute :compact, :boolean, default: false
20
+ attribute :basic, :boolean, default: false
21
+ attribute :equal_width, :boolean, default: false
22
+ attribute :wrapping, :boolean, default: false
23
+
24
+ def to_s
25
+ classes = class_names(
26
+ "ui",
27
+ aligned,
28
+ { "compact" => compact,
29
+ "basic" => basic, "equal width" => equal_width,
30
+ "wrapping" => wrapping },
31
+ "segments"
32
+ )
33
+
34
+ tag.div(class: classes) { @content }
35
+ end
36
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Shape — 3D CSS shape transformations.
4
+ #
5
+ # Usage:
6
+ # Shape(type: :cube) {
7
+ # text '<div class="sides">'.html_safe
8
+ # text '<div class="side active">Side 1</div>'.html_safe
9
+ # text '<div class="side">Side 2</div>'.html_safe
10
+ # text '</div>'.html_safe
11
+ # }
12
+
13
+ class ShapeComponent < Component
14
+ attribute :type, :string, default: nil
15
+
16
+ def to_s
17
+ classes = class_names(
18
+ "ui",
19
+ type,
20
+ "shape"
21
+ )
22
+
23
+ tag.div(class: classes, data: { controller: "fui-shape" }) { @content }
24
+ end
25
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Sidebar — slide-out sidebar navigation.
4
+ #
5
+ # Usage:
6
+ # Sidebar(direction: :left, visible: true, inverted: true) {
7
+ # MenuItem(href: "#") { text "Home" }
8
+ # MenuItem(href: "#") { text "Settings" }
9
+ # }
10
+
11
+ class SidebarComponent < Component
12
+ attribute :direction, :string, default: "left"
13
+ attribute :transition, :string, default: "overlay"
14
+ attribute :visible, :boolean, default: false
15
+ attribute :inverted, :boolean, default: false
16
+ attribute :vertical, :boolean, default: true
17
+ attribute :width, :string, default: nil
18
+
19
+ def to_s
20
+ classes = class_names(
21
+ "ui",
22
+ direction,
23
+ width,
24
+ { "inverted" => inverted,
25
+ "vertical" => vertical,
26
+ "visible" => visible },
27
+ "sidebar",
28
+ "menu"
29
+ )
30
+
31
+ tag.div(class: classes, data: { controller: "fui-sidebar", fui_sidebar_transition_value: transition }) { @content }
32
+ end
33
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Site — base site wrapper with Fomantic-UI site module.
4
+ #
5
+ # Wraps content in a container that initializes the Fomantic-UI site module
6
+ # via the fui-site Stimulus controller.
7
+
8
+ class SiteComponent < Component
9
+ def to_s
10
+ tag.div(data: { controller: "fui-site" }) { @content }
11
+ end
12
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Slider — range slider control.
4
+ #
5
+ # Usage:
6
+ # Slider(min: 0, max: 100, value: 50, name: "volume")
7
+ # Slider(min: 0, max: 10, step: 1, labeled: true, color: :blue)
8
+
9
+ class SliderComponent < Component
10
+ attribute :min, :integer, default: 0
11
+ attribute :max, :integer, default: 100
12
+ attribute :value, :integer, default: 0
13
+ attribute :step, :integer, default: 1
14
+ attribute :labeled, :boolean, default: false
15
+ attribute :vertical, :boolean, default: false
16
+ attribute :reversed, :boolean, default: false
17
+ attribute :color, :string, default: nil
18
+ attribute :name, :string, default: nil
19
+ attribute :disabled, :boolean, default: false
20
+
21
+ def to_s
22
+ classes = class_names(
23
+ "ui",
24
+ color,
25
+ { "labeled" => labeled,
26
+ "vertical" => vertical,
27
+ "reversed" => reversed,
28
+ "disabled" => disabled },
29
+ "slider"
30
+ )
31
+
32
+ data = {
33
+ controller: "fui-slider",
34
+ fui_slider_min_value: min,
35
+ fui_slider_max_value: max,
36
+ fui_slider_start_value: value,
37
+ fui_slider_step_value: step
38
+ }
39
+
40
+ name_el = name ? tag.input(type: "hidden", name: name, value: value) : nil
41
+
42
+ tag.div(class: classes, data: data) {
43
+ safe_join([ name_el, @content ])
44
+ }
45
+ end
46
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ # State — UI state management for elements.
4
+ #
5
+ # Usage:
6
+ # State(text_active: "Following", text_inactive: "Follow") {
7
+ # Button { text "Follow" }
8
+ # }
9
+
10
+ class StateComponent < Component
11
+ attribute :text_active, :string, default: nil
12
+ attribute :text_inactive, :string, default: nil
13
+ attribute :class_active, :string, default: nil
14
+ attribute :class_inactive, :string, default: nil
15
+
16
+ def to_s
17
+ data = { controller: "fui-state" }
18
+ data[:fui_state_text_active_value] = text_active if text_active
19
+ data[:fui_state_text_inactive_value] = text_inactive if text_inactive
20
+ data[:fui_state_class_active_value] = class_active if class_active
21
+ data[:fui_state_class_inactive_value] = class_inactive if class_inactive
22
+
23
+ tag.div(data: data) { @content }
24
+ end
25
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Statistic — numeric statistics display.
4
+ #
5
+ # Usage:
6
+ # Statistic(color: :blue) { |c|
7
+ # c.value { text "5,550" }
8
+ # c.label { text "Downloads" }
9
+ # }
10
+ # Statistic(horizontal: true) { |c|
11
+ # c.value { text "22" }
12
+ # c.label { text "Members" }
13
+ # }
14
+
15
+ class StatisticComponent < Component
16
+ attribute :horizontal, :boolean, default: false
17
+ attribute :color, :string, default: nil
18
+ attribute :size, :string, default: nil
19
+ attribute :inverted, :boolean, default: false
20
+ attribute :floated, :string, default: nil
21
+
22
+ slot :value
23
+ slot :label
24
+
25
+ def to_s
26
+ classes = class_names(
27
+ "ui",
28
+ color,
29
+ size,
30
+ floated && "#{floated} floated",
31
+ { "horizontal" => horizontal,
32
+ "inverted" => inverted },
33
+ "statistic"
34
+ )
35
+
36
+ value_el = @slots[:value] ? tag.div(class: "value") { @slots[:value] } : nil
37
+ label_el = @slots[:label] ? tag.div(class: "label") { @slots[:label] } : nil
38
+
39
+ tag.div(class: classes) {
40
+ safe_join([ value_el, label_el, @content.presence ])
41
+ }
42
+ end
43
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Step — individual step within a StepGroup.
4
+ #
5
+ # Usage:
6
+ # StepGroup {
7
+ # Step(active: true, icon: "truck", title: "Shipping", description: "Choose shipping")
8
+ # Step(title: "Billing", description: "Enter billing info")
9
+ # Step(disabled: true, title: "Confirm Order")
10
+ # }
11
+ #
12
+ # StepGroup {
13
+ # Step(completed: true) {
14
+ # text "Custom content"
15
+ # }
16
+ # }
17
+
18
+ class StepComponent < Component
19
+ attribute :active, :boolean, default: false
20
+ attribute :completed, :boolean, default: false
21
+ attribute :disabled, :boolean, default: false
22
+ attribute :icon, :string, default: nil
23
+ attribute :title, :string, default: nil
24
+ attribute :description, :string, default: nil
25
+ attribute :href, :string, default: nil
26
+
27
+ def to_s
28
+ classes = class_names(
29
+ { "active" => active, "completed" => completed, "disabled" => disabled },
30
+ "step"
31
+ )
32
+
33
+ icon_el = icon ? tag.i(class: "#{icon} icon") : nil
34
+
35
+ content_parts = [
36
+ (title ? tag.div(class: "title") { title } : nil),
37
+ (description ? tag.div(class: "description") { description } : nil)
38
+ ].compact
39
+
40
+ content_el = if content_parts.any?
41
+ tag.div(class: "content") { safe_join(content_parts) }
42
+ end
43
+
44
+ inner = if @content.present?
45
+ @content
46
+ else
47
+ safe_join([ icon_el, content_el ])
48
+ end
49
+
50
+ if href
51
+ tag.a(class: classes, href: href) { inner }
52
+ else
53
+ tag.div(class: classes) { inner }
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ # StepGroup — container for step indicators in multi-step workflows.
4
+ #
5
+ # Usage:
6
+ # StepGroup(ordered: true) {
7
+ # Step(active: true, title: "Shipping", description: "Choose your shipping options")
8
+ # Step(title: "Billing", description: "Enter billing information")
9
+ # Step(disabled: true, title: "Confirm Order")
10
+ # }
11
+
12
+ class StepGroupComponent < Component
13
+ attribute :ordered, :boolean, default: false
14
+ attribute :vertical, :boolean, default: false
15
+ attribute :fluid, :boolean, default: false
16
+ attribute :attached, :string, default: nil
17
+ attribute :unstackable, :boolean, default: false
18
+ attribute :size, :string, default: nil
19
+ attribute :stackable, :boolean, default: false
20
+ attribute :circular, :boolean, default: false
21
+
22
+ def to_s
23
+ classes = class_names(
24
+ "ui",
25
+ size,
26
+ attached && "#{attached} attached",
27
+ { "ordered" => ordered,
28
+ "vertical" => vertical,
29
+ "fluid" => fluid,
30
+ "unstackable" => unstackable,
31
+ "stackable" => stackable,
32
+ "circular" => circular },
33
+ "steps"
34
+ )
35
+
36
+ tag.div(class: classes) { @content }
37
+ end
38
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Sticky — sticky/fixed position elements that follow scroll.
4
+ #
5
+ # Usage:
6
+ # Sticky { text "I stick on scroll" }
7
+ # Sticky(offset: 50, pushing: true) { text "Sticky header" }
8
+
9
+ class StickyComponent < Component
10
+ attribute :context, :string, default: nil
11
+ attribute :pushing, :boolean, default: false
12
+ attribute :offset, :integer, default: 0
13
+
14
+ def to_s
15
+ data = { controller: "fui-sticky" }
16
+ data[:fui_sticky_context_value] = context if context
17
+ data[:fui_sticky_pushing_value] = "true" if pushing
18
+ data[:fui_sticky_offset_value] = offset if offset > 0
19
+
20
+ tag.div(class: "ui sticky", data: data) { @content }
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SubHeader — sub header text within a Header.
4
+ #
5
+ # Usage:
6
+ # Header(size: :h2) {
7
+ # text "Main Title"
8
+ # SubHeader { text "A subtitle or description" }
9
+ # }
10
+
11
+ class SubHeaderComponent < Component
12
+ def to_s
13
+ tag.div(class: "sub header") { @content }
14
+ end
15
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SubMenu — sub-menu or positioned menu group within a Menu.
4
+ #
5
+ # Usage:
6
+ # Menu {
7
+ # MenuItem(href: "/") { text "Home" }
8
+ # SubMenu(position: "right") {
9
+ # MenuItem(href: "/login") { text "Login" }
10
+ # }
11
+ # }
12
+
13
+ class SubMenuComponent < Component
14
+ include Positionable
15
+
16
+ def to_s
17
+ classes = class_names(
18
+ position,
19
+ "menu"
20
+ )
21
+
22
+ tag.div(class: classes) { @content }
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Tab — tab navigation with content panes.
4
+ #
5
+ # Usage:
6
+ # Tab(active: true) { text "Tab pane content" }
7
+
8
+ class TabComponent < Component
9
+ attribute :active, :boolean, default: false
10
+ attribute :path, :string, default: nil
11
+
12
+ def to_s
13
+ classes = class_names(
14
+ "ui",
15
+ { "active" => active },
16
+ "tab"
17
+ )
18
+
19
+ data = { controller: "fui-tab" }
20
+ data[:tab] = path if path
21
+
22
+ tag.div(class: classes, data: data) { @content }
23
+ end
24
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ # TableCell — a table cell (td or th).
4
+ #
5
+ # Usage:
6
+ # TableRow {
7
+ # TableCell { text "Data" }
8
+ # TableCell(aligned: "right", collapsing: true) { text "10 hours ago" }
9
+ # TableCell(heading: true, colspan: 3) { text "Section" }
10
+ # }
11
+
12
+ class TableCellComponent < Component
13
+ attribute :heading, :boolean, default: false
14
+ attribute :aligned, :string, default: nil
15
+ attribute :collapsing, :boolean, default: false
16
+ attribute :single_line, :boolean, default: false
17
+ attribute :positive, :boolean, default: false
18
+ attribute :negative, :boolean, default: false
19
+ attribute :warning, :boolean, default: false
20
+ attribute :error, :boolean, default: false
21
+ attribute :active, :boolean, default: false
22
+ attribute :disabled, :boolean, default: false
23
+ attribute :selectable, :boolean, default: false
24
+ attribute :color, :string, default: nil
25
+ attribute :width, :integer, default: nil
26
+ attribute :colspan, :integer, default: nil
27
+ attribute :rowspan, :integer, default: nil
28
+
29
+ NUMBERS = %w[one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen sixteen].freeze
30
+
31
+ def to_s
32
+ width_word = width && width.between?(1, 16) ? NUMBERS[width - 1] : nil
33
+
34
+ classes = [
35
+ (aligned && "#{aligned} aligned"),
36
+ ("collapsing" if collapsing),
37
+ ("single line" if single_line),
38
+ ("positive" if positive),
39
+ ("negative" if negative),
40
+ ("warning" if warning),
41
+ ("error" if error),
42
+ ("active" if active),
43
+ ("disabled" if disabled),
44
+ ("selectable" if selectable),
45
+ color,
46
+ (width_word && "#{width_word} wide")
47
+ ].compact
48
+
49
+ opts = {}
50
+ opts[:class] = classes.join(" ") if classes.any?
51
+ opts[:colspan] = colspan if colspan
52
+ opts[:rowspan] = rowspan if rowspan
53
+
54
+ if heading
55
+ tag.th(**opts) { @content }
56
+ else
57
+ tag.td(**opts) { @content }
58
+ end
59
+ end
60
+ end