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.
- checksums.yaml +7 -0
- data/Rakefile +6 -0
- data/app/assets/stylesheets.css +73555 -0
- data/app/components/accordion_component.rb +34 -0
- data/app/components/ad_component.rb +28 -0
- data/app/components/api_component.rb +24 -0
- data/app/components/breadcrumb_component.rb +26 -0
- data/app/components/button_component.rb +49 -0
- data/app/components/calendar_component.rb +34 -0
- data/app/components/card_component.rb +56 -0
- data/app/components/checkbox_component.rb +41 -0
- data/app/components/column_component.rb +62 -0
- data/app/components/comment_component.rb +45 -0
- data/app/components/concerns/alignable.rb +21 -0
- data/app/components/concerns/attachable.rb +16 -0
- data/app/components/concerns/orientable.rb +21 -0
- data/app/components/concerns/positionable.rb +21 -0
- data/app/components/concerns/sizeable.rb +18 -0
- data/app/components/container_component.rb +23 -0
- data/app/components/dimmer_component.rb +30 -0
- data/app/components/divider_component.rb +30 -0
- data/app/components/dropdown_component.rb +63 -0
- data/app/components/embed_component.rb +32 -0
- data/app/components/emoji_component.rb +15 -0
- data/app/components/feed_component.rb +22 -0
- data/app/components/flag_component.rb +15 -0
- data/app/components/flyout_component.rb +41 -0
- data/app/components/form_component.rb +39 -0
- data/app/components/grid_component.rb +85 -0
- data/app/components/h_stack_component.rb +67 -0
- data/app/components/header_component.rb +60 -0
- data/app/components/icon_component.rb +41 -0
- data/app/components/image_component.rb +46 -0
- data/app/components/input_component.rb +52 -0
- data/app/components/item_component.rb +39 -0
- data/app/components/item_group_component.rb +30 -0
- data/app/components/label_component.rb +49 -0
- data/app/components/link_component.rb +23 -0
- data/app/components/list_component.rb +39 -0
- data/app/components/loader_component.rb +33 -0
- data/app/components/menu_component.rb +64 -0
- data/app/components/menu_item_component.rb +52 -0
- data/app/components/message_component.rb +54 -0
- data/app/components/modal_component.rb +50 -0
- data/app/components/nag_component.rb +25 -0
- data/app/components/overlay_component.rb +16 -0
- data/app/components/placeholder_component.rb +39 -0
- data/app/components/popup_component.rb +31 -0
- data/app/components/progress_component.rb +48 -0
- data/app/components/pusher_component.rb +18 -0
- data/app/components/rail_component.rb +31 -0
- data/app/components/rating_component.rb +41 -0
- data/app/components/reset_component.rb +12 -0
- data/app/components/reveal_component.rb +39 -0
- data/app/components/row_component.rb +39 -0
- data/app/components/search_component.rb +44 -0
- data/app/components/segment_component.rb +57 -0
- data/app/components/segment_group_component.rb +36 -0
- data/app/components/shape_component.rb +25 -0
- data/app/components/sidebar_component.rb +33 -0
- data/app/components/site_component.rb +12 -0
- data/app/components/slider_component.rb +46 -0
- data/app/components/state_component.rb +25 -0
- data/app/components/statistic_component.rb +43 -0
- data/app/components/step_component.rb +56 -0
- data/app/components/step_group_component.rb +38 -0
- data/app/components/sticky_component.rb +22 -0
- data/app/components/sub_header_component.rb +15 -0
- data/app/components/sub_menu_component.rb +24 -0
- data/app/components/tab_component.rb +24 -0
- data/app/components/table_cell_component.rb +60 -0
- data/app/components/table_component.rb +160 -0
- data/app/components/table_row_component.rb +43 -0
- data/app/components/text_component.rb +73 -0
- data/app/components/toast_component.rb +36 -0
- data/app/components/transition_component.rb +32 -0
- data/app/components/v_stack_component.rb +31 -0
- data/app/components/visibility_component.rb +22 -0
- data/app/helpers/component_helper.rb +109 -0
- data/app/helpers/fui_helper.rb +53 -0
- data/app/javascript/accordion.js +547 -0
- data/app/javascript/accordion.min.js +11 -0
- data/app/javascript/api.js +1112 -0
- data/app/javascript/api.min.js +11 -0
- data/app/javascript/calendar.js +1960 -0
- data/app/javascript/calendar.min.js +11 -0
- data/app/javascript/checkbox.js +819 -0
- data/app/javascript/checkbox.min.js +11 -0
- data/app/javascript/dimmer.js +686 -0
- data/app/javascript/dimmer.min.js +11 -0
- data/app/javascript/dropdown.js +4019 -0
- data/app/javascript/dropdown.min.js +11 -0
- data/app/javascript/embed.js +646 -0
- data/app/javascript/embed.min.js +11 -0
- data/app/javascript/flyout.js +1405 -0
- data/app/javascript/flyout.min.js +11 -0
- data/app/javascript/form.js +2070 -0
- data/app/javascript/form.min.js +11 -0
- data/app/javascript/jquery.js +10716 -0
- data/app/javascript/jquery.min.js +2 -0
- data/app/javascript/modal.js +1507 -0
- data/app/javascript/modal.min.js +11 -0
- data/app/javascript/nag.js +522 -0
- data/app/javascript/nag.min.js +11 -0
- data/app/javascript/popup.js +1457 -0
- data/app/javascript/popup.min.js +11 -0
- data/app/javascript/progress.js +922 -0
- data/app/javascript/progress.min.js +11 -0
- data/app/javascript/rating.js +496 -0
- data/app/javascript/rating.min.js +11 -0
- data/app/javascript/search.js +1519 -0
- data/app/javascript/search.min.js +11 -0
- data/app/javascript/shape.js +721 -0
- data/app/javascript/shape.min.js +11 -0
- data/app/javascript/sidebar.js +952 -0
- data/app/javascript/sidebar.min.js +11 -0
- data/app/javascript/site.js +415 -0
- data/app/javascript/site.min.js +11 -0
- data/app/javascript/slider.js +1449 -0
- data/app/javascript/slider.min.js +11 -0
- data/app/javascript/state.js +653 -0
- data/app/javascript/state.min.js +11 -0
- data/app/javascript/sticky.js +852 -0
- data/app/javascript/sticky.min.js +11 -0
- data/app/javascript/tab.js +867 -0
- data/app/javascript/tab.min.js +11 -0
- data/app/javascript/toast.js +916 -0
- data/app/javascript/toast.min.js +11 -0
- data/app/javascript/transition.js +955 -0
- data/app/javascript/transition.min.js +11 -0
- data/app/javascript/ui/controllers/fui_accordion_controller.js +45 -0
- data/app/javascript/ui/controllers/fui_api_controller.js +80 -0
- data/app/javascript/ui/controllers/fui_calendar_controller.js +66 -0
- data/app/javascript/ui/controllers/fui_checkbox_controller.js +48 -0
- data/app/javascript/ui/controllers/fui_dimmer_controller.js +45 -0
- data/app/javascript/ui/controllers/fui_dropdown_controller.js +68 -0
- data/app/javascript/ui/controllers/fui_embed_controller.js +49 -0
- data/app/javascript/ui/controllers/fui_flyout_controller.js +49 -0
- data/app/javascript/ui/controllers/fui_form_controller.js +62 -0
- data/app/javascript/ui/controllers/fui_modal_controller.js +61 -0
- data/app/javascript/ui/controllers/fui_nag_controller.js +52 -0
- data/app/javascript/ui/controllers/fui_popup_controller.js +58 -0
- data/app/javascript/ui/controllers/fui_progress_controller.js +60 -0
- data/app/javascript/ui/controllers/fui_rating_controller.js +49 -0
- data/app/javascript/ui/controllers/fui_search_controller.js +76 -0
- data/app/javascript/ui/controllers/fui_shape_controller.js +45 -0
- data/app/javascript/ui/controllers/fui_sidebar_controller.js +48 -0
- data/app/javascript/ui/controllers/fui_site_controller.js +29 -0
- data/app/javascript/ui/controllers/fui_slider_controller.js +53 -0
- data/app/javascript/ui/controllers/fui_state_controller.js +63 -0
- data/app/javascript/ui/controllers/fui_sticky_controller.js +50 -0
- data/app/javascript/ui/controllers/fui_tab_controller.js +57 -0
- data/app/javascript/ui/controllers/fui_toast_controller.js +60 -0
- data/app/javascript/ui/controllers/fui_transition_controller.js +60 -0
- data/app/javascript/ui/controllers/fui_visibility_controller.js +55 -0
- data/app/javascript/ui/index.js +114 -0
- data/app/javascript/visibility.js +1196 -0
- data/app/javascript/visibility.min.js +11 -0
- data/app/lib/component.rb +63 -0
- data/config/importmap.rb +27 -0
- data/config/initializers/ruby_template_handler.rb +31 -0
- data/config/routes.rb +2 -0
- data/lib/tasks/ui_tasks.rake +4 -0
- data/lib/ui/engine.rb +27 -0
- data/lib/ui/version.rb +3 -0
- data/lib/ui.rb +6 -0
- metadata +220 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Accordion — collapsible content panels.
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# Accordion(styled: true) {
|
|
7
|
+
# MenuItem(header: true) {
|
|
8
|
+
# Icon(name: "dropdown")
|
|
9
|
+
# text " Section"
|
|
10
|
+
# }
|
|
11
|
+
# text "Panel content"
|
|
12
|
+
# }
|
|
13
|
+
|
|
14
|
+
class AccordionComponent < Component
|
|
15
|
+
attribute :styled, :boolean, default: false
|
|
16
|
+
attribute :fluid, :boolean, default: false
|
|
17
|
+
attribute :inverted, :boolean, default: false
|
|
18
|
+
attribute :exclusive, :boolean, default: true
|
|
19
|
+
attribute :collapsible, :boolean, default: true
|
|
20
|
+
|
|
21
|
+
def to_s
|
|
22
|
+
classes = class_names(
|
|
23
|
+
"ui",
|
|
24
|
+
{ "styled" => styled, "fluid" => fluid, "inverted" => inverted },
|
|
25
|
+
"accordion"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
data = { controller: "fui-accordion" }
|
|
29
|
+
data[:fui_accordion_exclusive_value] = "false" unless exclusive
|
|
30
|
+
data[:fui_accordion_collapsible_value] = "false" unless collapsible
|
|
31
|
+
|
|
32
|
+
tag.div(class: classes, data: data) { @content }
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Ad — standard ad unit sizes following IAB standards.
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# Ad(unit: :leaderboard)
|
|
7
|
+
# Ad(unit: :medium_rectangle, test: "Ad Placeholder")
|
|
8
|
+
|
|
9
|
+
class AdComponent < Component
|
|
10
|
+
attribute :unit, :string, default: nil
|
|
11
|
+
attribute :test, :string, default: nil
|
|
12
|
+
|
|
13
|
+
def to_s
|
|
14
|
+
unit_class = unit.to_s.tr("_", " ")
|
|
15
|
+
|
|
16
|
+
classes = class_names(
|
|
17
|
+
"ui",
|
|
18
|
+
unit_class,
|
|
19
|
+
{ "test" => test },
|
|
20
|
+
"ad"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
opts = { class: classes }
|
|
24
|
+
opts[:data] = { text: test } if test
|
|
25
|
+
|
|
26
|
+
tag.div(**opts) { @content }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Api — AJAX request management wrapper.
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# Api(url: "/api/users", method_val: :get) {
|
|
7
|
+
# Button { text "Load Users" }
|
|
8
|
+
# }
|
|
9
|
+
|
|
10
|
+
class ApiComponent < Component
|
|
11
|
+
attribute :url, :string, default: nil
|
|
12
|
+
attribute :method_val, :string, default: "get"
|
|
13
|
+
attribute :action_val, :string, default: nil
|
|
14
|
+
attribute :state_context, :string, default: nil
|
|
15
|
+
|
|
16
|
+
def to_s
|
|
17
|
+
data = { controller: "fui-api", fui_api_method_value: method_val }
|
|
18
|
+
data[:fui_api_url_value] = url if url
|
|
19
|
+
data[:fui_api_action_value] = action_val if action_val
|
|
20
|
+
data[:fui_api_state_context_value] = state_context if state_context
|
|
21
|
+
|
|
22
|
+
tag.div(data: data) { @content }
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Breadcrumb — navigation breadcrumb trail.
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# Breadcrumb {
|
|
7
|
+
# text '<a class="section">Home</a>'.html_safe
|
|
8
|
+
# text '<div class="divider">/</div>'.html_safe
|
|
9
|
+
# text '<div class="active section">Current</div>'.html_safe
|
|
10
|
+
# }
|
|
11
|
+
# Breadcrumb(size: :large)
|
|
12
|
+
|
|
13
|
+
class BreadcrumbComponent < Component
|
|
14
|
+
attribute :size, :string, default: nil
|
|
15
|
+
attribute :divider, :string, default: nil
|
|
16
|
+
|
|
17
|
+
def to_s
|
|
18
|
+
classes = class_names(
|
|
19
|
+
"ui",
|
|
20
|
+
size,
|
|
21
|
+
"breadcrumb"
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
tag.div(class: classes) { @content }
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Button — buttons in many styles.
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# Button(color: :green, icon: "checkmark") { text "Approve" }
|
|
7
|
+
# Button(variant: :primary, size: :large, fluid: true) { text "Submit" }
|
|
8
|
+
# Button(href: "/path", variant: :primary) { text "Go" }
|
|
9
|
+
|
|
10
|
+
class ButtonComponent < Component
|
|
11
|
+
include Attachable
|
|
12
|
+
include Sizeable
|
|
13
|
+
|
|
14
|
+
attribute :color, :string, default: nil
|
|
15
|
+
attribute :variant, :string, default: nil
|
|
16
|
+
attribute :icon, :string, default: nil
|
|
17
|
+
attribute :fluid, :boolean, default: false
|
|
18
|
+
attribute :disabled, :boolean, default: false
|
|
19
|
+
attribute :loading, :boolean, default: false
|
|
20
|
+
attribute :animated, :string, default: nil
|
|
21
|
+
attribute :labeled, :boolean, default: false
|
|
22
|
+
attribute :href, :string, default: nil
|
|
23
|
+
attribute :type, :string, default: "button"
|
|
24
|
+
attribute :inverted, :boolean, default: false
|
|
25
|
+
|
|
26
|
+
def to_s
|
|
27
|
+
classes = class_names(
|
|
28
|
+
"ui",
|
|
29
|
+
color,
|
|
30
|
+
size,
|
|
31
|
+
variant,
|
|
32
|
+
animated && "animated #{animated unless animated == 'true'}".strip,
|
|
33
|
+
{ "fluid" => fluid, "disabled" => disabled, "loading" => loading,
|
|
34
|
+
"inverted" => inverted, "attached" => attached,
|
|
35
|
+
"labeled icon" => labeled && icon,
|
|
36
|
+
"icon" => icon && !labeled && !@content },
|
|
37
|
+
"button"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
icon_el = icon ? tag.i(class: "#{icon} icon") : nil
|
|
41
|
+
content = safe_join([ icon_el, @content ])
|
|
42
|
+
|
|
43
|
+
if href
|
|
44
|
+
tag.a(class: classes, href: href) { content }
|
|
45
|
+
else
|
|
46
|
+
tag.button(class: classes, type: type) { content }
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Calendar — date/time picker.
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# Calendar(type: :date, name: "start_date")
|
|
7
|
+
# Calendar(type: :datetime, format: "YYYY-MM-DD HH:mm")
|
|
8
|
+
|
|
9
|
+
class CalendarComponent < Component
|
|
10
|
+
attribute :type, :string, default: "date"
|
|
11
|
+
attribute :name, :string, default: nil
|
|
12
|
+
attribute :format, :string, default: nil
|
|
13
|
+
attribute :min_date, :string, default: nil
|
|
14
|
+
attribute :max_date, :string, default: nil
|
|
15
|
+
|
|
16
|
+
def to_s
|
|
17
|
+
data = { controller: "fui-calendar", fui_calendar_type_value: type }
|
|
18
|
+
data[:fui_calendar_format_value] = format if format
|
|
19
|
+
data[:fui_calendar_min_date_value] = min_date if min_date
|
|
20
|
+
data[:fui_calendar_max_date_value] = max_date if max_date
|
|
21
|
+
|
|
22
|
+
input_opts = { type: "text", placeholder: type }
|
|
23
|
+
input_opts[:name] = name if name
|
|
24
|
+
|
|
25
|
+
tag.div(class: "ui calendar", data: data) {
|
|
26
|
+
tag.div(class: "ui input left icon") {
|
|
27
|
+
safe_join([
|
|
28
|
+
tag.i(class: "calendar icon"),
|
|
29
|
+
tag.input(**input_opts)
|
|
30
|
+
])
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Card — content cards with multiple regions.
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# Card(fluid: true) { |c|
|
|
7
|
+
# c.image { Image(src: "photo.jpg") }
|
|
8
|
+
# c.header { text "Title" }
|
|
9
|
+
# c.meta { text "Subtitle" }
|
|
10
|
+
# c.description { text "Body text" }
|
|
11
|
+
# c.extra { text "Extra content" }
|
|
12
|
+
# }
|
|
13
|
+
|
|
14
|
+
class CardComponent < Component
|
|
15
|
+
attribute :fluid, :boolean, default: false
|
|
16
|
+
attribute :centered, :boolean, default: false
|
|
17
|
+
attribute :raised, :boolean, default: false
|
|
18
|
+
attribute :link, :boolean, default: false
|
|
19
|
+
attribute :color, :string, default: nil
|
|
20
|
+
attribute :href, :string, default: nil
|
|
21
|
+
|
|
22
|
+
slot :image
|
|
23
|
+
slot :header
|
|
24
|
+
slot :meta
|
|
25
|
+
slot :description
|
|
26
|
+
slot :extra
|
|
27
|
+
|
|
28
|
+
def to_s
|
|
29
|
+
classes = class_names(
|
|
30
|
+
"ui",
|
|
31
|
+
color,
|
|
32
|
+
{ "fluid" => fluid, "centered" => centered, "raised" => raised,
|
|
33
|
+
"link" => link || href },
|
|
34
|
+
"card"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
image_el = @slots[:image] ? tag.div(class: "image") { @slots[:image] } : nil
|
|
38
|
+
|
|
39
|
+
content_parts = [
|
|
40
|
+
@slots[:header] ? tag.div(class: "header") { @slots[:header] } : nil,
|
|
41
|
+
@slots[:meta] ? tag.div(class: "meta") { @slots[:meta] } : nil,
|
|
42
|
+
@slots[:description] ? tag.div(class: "description") { @slots[:description] } : nil
|
|
43
|
+
].compact
|
|
44
|
+
|
|
45
|
+
content_el = content_parts.any? ? tag.div(class: "content") { safe_join(content_parts) } : nil
|
|
46
|
+
extra_el = @slots[:extra] ? tag.div(class: "extra content") { @slots[:extra] } : nil
|
|
47
|
+
|
|
48
|
+
inner = safe_join([ image_el, content_el, @content.presence, extra_el ])
|
|
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,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Checkbox — checkboxes, radios, toggles, sliders.
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# Checkbox(label_text: "Accept terms", name: "terms")
|
|
7
|
+
# Checkbox(type: :toggle, label_text: "Dark mode", checked: true)
|
|
8
|
+
# Checkbox(type: :radio, label_text: "Option A", name: "choice", value: "a")
|
|
9
|
+
|
|
10
|
+
class CheckboxComponent < Component
|
|
11
|
+
attribute :type, :string, default: "checkbox"
|
|
12
|
+
attribute :label_text, :string, default: nil
|
|
13
|
+
attribute :name, :string, default: nil
|
|
14
|
+
attribute :value, :string, default: nil
|
|
15
|
+
attribute :checked, :boolean, default: false
|
|
16
|
+
attribute :disabled, :boolean, default: false
|
|
17
|
+
attribute :read_only, :boolean, default: false
|
|
18
|
+
attribute :fitted, :boolean, default: false
|
|
19
|
+
|
|
20
|
+
def to_s
|
|
21
|
+
classes = class_names(
|
|
22
|
+
"ui",
|
|
23
|
+
(type unless type == "checkbox"),
|
|
24
|
+
{ "fitted" => fitted, "read-only" => read_only, "disabled" => disabled },
|
|
25
|
+
"checkbox"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
field_type = type == "radio" ? "radio" : "checkbox"
|
|
29
|
+
input_opts = { type: field_type }
|
|
30
|
+
input_opts[:name] = name if name
|
|
31
|
+
input_opts[:value] = value if value
|
|
32
|
+
input_opts[:checked] = "checked" if checked
|
|
33
|
+
input_opts[:disabled] = "disabled" if disabled
|
|
34
|
+
|
|
35
|
+
label_el = tag.label { label_text || "" }
|
|
36
|
+
|
|
37
|
+
tag.div(class: classes, data: { controller: "fui-checkbox" }) {
|
|
38
|
+
safe_join([ tag.input(**input_opts), label_el ])
|
|
39
|
+
}
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Column — grid column with responsive width support.
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# Grid(stackable: true) {
|
|
7
|
+
# Column(width: 4) { text "Four wide" }
|
|
8
|
+
# Column(computer: 8, tablet: 16) { text "Responsive" }
|
|
9
|
+
# Column(computer: 4, tablet: 8, mobile: 16, aligned: "center") { text "All breakpoints" }
|
|
10
|
+
# }
|
|
11
|
+
|
|
12
|
+
class ColumnComponent < Component
|
|
13
|
+
NUMBERS = %w[one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen sixteen].freeze
|
|
14
|
+
|
|
15
|
+
attribute :width, :integer, default: nil
|
|
16
|
+
attribute :computer, :integer, default: nil
|
|
17
|
+
attribute :tablet, :integer, default: nil
|
|
18
|
+
attribute :mobile, :integer, default: nil
|
|
19
|
+
attribute :widescreen, :integer, default: nil
|
|
20
|
+
attribute :large_screen, :integer, default: nil
|
|
21
|
+
attribute :aligned, :string, default: nil
|
|
22
|
+
attribute :floated, :string, default: nil
|
|
23
|
+
attribute :only, :string, default: nil
|
|
24
|
+
attribute :color, :string, default: nil
|
|
25
|
+
|
|
26
|
+
def to_s
|
|
27
|
+
# Cannot use class_names here — it deduplicates tokens, which breaks
|
|
28
|
+
# responsive widths where "wide" appears for each breakpoint.
|
|
29
|
+
classes = [
|
|
30
|
+
width_class(width, nil),
|
|
31
|
+
width_class(computer, "computer"),
|
|
32
|
+
width_class(tablet, "tablet"),
|
|
33
|
+
width_class(mobile, "mobile"),
|
|
34
|
+
width_class(widescreen, "widescreen"),
|
|
35
|
+
width_class(large_screen, "large screen"),
|
|
36
|
+
only_class,
|
|
37
|
+
(aligned && "#{aligned} aligned"),
|
|
38
|
+
(floated && "#{floated} floated"),
|
|
39
|
+
color,
|
|
40
|
+
"column"
|
|
41
|
+
].compact.join(" ")
|
|
42
|
+
|
|
43
|
+
tag.div(class: classes) { @content }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
# Supports single ("mobile") or multiple ("tablet mobile") only values.
|
|
49
|
+
# Each device word gets "only" appended: "tablet only mobile only"
|
|
50
|
+
def only_class
|
|
51
|
+
return unless only
|
|
52
|
+
|
|
53
|
+
only.split.map { |device| "#{device} only" }.join(" ")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def width_class(value, device)
|
|
57
|
+
return unless value && value.between?(1, 16)
|
|
58
|
+
|
|
59
|
+
word = NUMBERS[value - 1]
|
|
60
|
+
device ? "#{word} wide #{device}" : "#{word} wide"
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Comment — threaded comment display.
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# Comment { |c|
|
|
7
|
+
# c.avatar { Image(src: "avatar.png") }
|
|
8
|
+
# c.author { text "Matt" }
|
|
9
|
+
# c.metadata { text "Today at 5:42PM" }
|
|
10
|
+
# c.text_slot { text "Great post!" }
|
|
11
|
+
# c.actions { text '<a class="reply">Reply</a>'.html_safe }
|
|
12
|
+
# }
|
|
13
|
+
|
|
14
|
+
class CommentComponent < Component
|
|
15
|
+
attribute :collapsed, :boolean, default: false
|
|
16
|
+
attribute :threaded, :boolean, default: false
|
|
17
|
+
|
|
18
|
+
slot :avatar
|
|
19
|
+
slot :author
|
|
20
|
+
slot :metadata
|
|
21
|
+
slot :text_slot
|
|
22
|
+
slot :actions
|
|
23
|
+
|
|
24
|
+
def to_s
|
|
25
|
+
classes = class_names(
|
|
26
|
+
{ "collapsed" => collapsed },
|
|
27
|
+
"comment"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
avatar_el = @slots[:avatar] ? tag.a(class: "avatar") { @slots[:avatar] } : nil
|
|
31
|
+
|
|
32
|
+
content_parts = [
|
|
33
|
+
@slots[:author] ? tag.a(class: "author") { @slots[:author] } : nil,
|
|
34
|
+
@slots[:metadata] ? tag.div(class: "metadata") { @slots[:metadata] } : nil,
|
|
35
|
+
@slots[:text_slot] ? tag.div(class: "text") { @slots[:text_slot] } : nil,
|
|
36
|
+
@slots[:actions] ? tag.div(class: "actions") { @slots[:actions] } : nil
|
|
37
|
+
].compact
|
|
38
|
+
|
|
39
|
+
content_el = content_parts.any? ? tag.div(class: "content") { safe_join(content_parts) } : nil
|
|
40
|
+
|
|
41
|
+
tag.div(class: classes) {
|
|
42
|
+
safe_join([ avatar_el, content_el, @content.presence ])
|
|
43
|
+
}
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Alignable — adds an `aligned` string attribute (vertical / horizontal).
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# class GridComponent < Component
|
|
7
|
+
# include Alignable # default: nil
|
|
8
|
+
# end
|
|
9
|
+
#
|
|
10
|
+
# class ColumnComponent < Component
|
|
11
|
+
# include Alignable
|
|
12
|
+
# default aligned: "vertical" # override default
|
|
13
|
+
# end
|
|
14
|
+
|
|
15
|
+
module Alignable
|
|
16
|
+
extend ActiveSupport::Concern
|
|
17
|
+
|
|
18
|
+
included do
|
|
19
|
+
attribute :aligned, :string, default: nil
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Attachable — adds an `attached` boolean attribute to a component.
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# class ButtonComponent < Component
|
|
7
|
+
# include Attachable
|
|
8
|
+
# end
|
|
9
|
+
|
|
10
|
+
module Attachable
|
|
11
|
+
extend ActiveSupport::Concern
|
|
12
|
+
|
|
13
|
+
included do
|
|
14
|
+
attribute :attached, :boolean, default: false
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Orientable — adds an `orientation` string attribute (vertical / horizontal).
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# class DividerComponent < Component
|
|
7
|
+
# include Orientable # default: nil
|
|
8
|
+
# end
|
|
9
|
+
#
|
|
10
|
+
# class MenuComponent < Component
|
|
11
|
+
# include Orientable
|
|
12
|
+
# default orientation: "horizontal" # override default
|
|
13
|
+
# end
|
|
14
|
+
|
|
15
|
+
module Orientable
|
|
16
|
+
extend ActiveSupport::Concern
|
|
17
|
+
|
|
18
|
+
included do
|
|
19
|
+
attribute :orientation, :string, default: nil
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Positionable — adds a `position` string attribute to a component.
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# class MenuComponent < Component
|
|
7
|
+
# include Positionable # default: nil
|
|
8
|
+
# end
|
|
9
|
+
#
|
|
10
|
+
# class RailComponent < Component
|
|
11
|
+
# include Positionable
|
|
12
|
+
# default position: "left" # override default
|
|
13
|
+
# end
|
|
14
|
+
|
|
15
|
+
module Positionable
|
|
16
|
+
extend ActiveSupport::Concern
|
|
17
|
+
|
|
18
|
+
included do
|
|
19
|
+
attribute :position, :string, default: nil
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Sizeable — adds a `size` string attribute.
|
|
4
|
+
#
|
|
5
|
+
# Valid sizes: mini, tiny, small, large, big, huge, massive
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# class ButtonComponent < Component
|
|
9
|
+
# include Sizeable
|
|
10
|
+
# end
|
|
11
|
+
|
|
12
|
+
module Sizeable
|
|
13
|
+
extend ActiveSupport::Concern
|
|
14
|
+
|
|
15
|
+
included do
|
|
16
|
+
attribute :size, :string, default: nil
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Container — fixed-width page container.
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# Container { text "Page content" }
|
|
7
|
+
# Container(text: true) { text "Narrower text container" }
|
|
8
|
+
# Container(fluid: true) { text "Full-width" }
|
|
9
|
+
|
|
10
|
+
class ContainerComponent < Component
|
|
11
|
+
attribute :text, :boolean, default: false
|
|
12
|
+
attribute :fluid, :boolean, default: false
|
|
13
|
+
|
|
14
|
+
def to_s
|
|
15
|
+
classes = class_names(
|
|
16
|
+
"ui",
|
|
17
|
+
{ "text" => text, "fluid" => fluid },
|
|
18
|
+
"container"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
tag.div(class: classes) { @content }
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Dimmer — page/element overlay dimmer.
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# Dimmer(active: true) { text "Dimmed content" }
|
|
7
|
+
# Dimmer(inverted: true, active: true) {
|
|
8
|
+
# Loader(active: true) { text "Loading..." }
|
|
9
|
+
# }
|
|
10
|
+
|
|
11
|
+
class DimmerComponent < Component
|
|
12
|
+
attribute :active, :boolean, default: false
|
|
13
|
+
attribute :inverted, :boolean, default: false
|
|
14
|
+
attribute :blurring, :boolean, default: false
|
|
15
|
+
attribute :simple, :boolean, default: false
|
|
16
|
+
attribute :page, :boolean, default: false
|
|
17
|
+
|
|
18
|
+
def to_s
|
|
19
|
+
classes = class_names(
|
|
20
|
+
"ui",
|
|
21
|
+
{ "active" => active, "inverted" => inverted, "blurring" => blurring,
|
|
22
|
+
"simple" => simple, "page" => page },
|
|
23
|
+
"dimmer"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
content_el = @content.present? ? tag.div(class: "content") { @content } : nil
|
|
27
|
+
|
|
28
|
+
tag.div(class: classes, data: { controller: "fui-dimmer" }) { content_el }
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Divider — horizontal/vertical dividers, optionally with text or icon.
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# Divider()
|
|
7
|
+
# Divider(horizontal: true) { text "OR" }
|
|
8
|
+
# Divider(section: true, inverted: true)
|
|
9
|
+
|
|
10
|
+
class DividerComponent < Component
|
|
11
|
+
attribute :vertical, :boolean, default: false
|
|
12
|
+
attribute :horizontal, :boolean, default: false
|
|
13
|
+
attribute :hidden, :boolean, default: false
|
|
14
|
+
attribute :section, :boolean, default: false
|
|
15
|
+
attribute :inverted, :boolean, default: false
|
|
16
|
+
attribute :fitted, :boolean, default: false
|
|
17
|
+
attribute :clearing, :boolean, default: false
|
|
18
|
+
|
|
19
|
+
def to_s
|
|
20
|
+
classes = class_names(
|
|
21
|
+
"ui",
|
|
22
|
+
{ "vertical" => vertical, "horizontal" => horizontal, "hidden" => hidden,
|
|
23
|
+
"section" => section, "inverted" => inverted, "fitted" => fitted,
|
|
24
|
+
"clearing" => clearing },
|
|
25
|
+
"divider"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
tag.div(class: classes) { @content }
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Dropdown — selection dropdowns with search and multi-select.
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# Dropdown(selection: true, placeholder: "Select...", name: "country") {
|
|
7
|
+
# MenuItem { text "United States" }
|
|
8
|
+
# MenuItem { text "Canada" }
|
|
9
|
+
# }
|
|
10
|
+
|
|
11
|
+
class DropdownComponent < Component
|
|
12
|
+
attribute :selection, :boolean, default: false
|
|
13
|
+
attribute :search, :boolean, default: false
|
|
14
|
+
attribute :multiple, :boolean, default: false
|
|
15
|
+
attribute :clearable, :boolean, default: false
|
|
16
|
+
attribute :fluid, :boolean, default: false
|
|
17
|
+
attribute :placeholder, :string, default: nil
|
|
18
|
+
attribute :name, :string, default: nil
|
|
19
|
+
attribute :default_value, :string, default: nil
|
|
20
|
+
attribute :pointing, :string, default: nil
|
|
21
|
+
attribute :compact, :boolean, default: false
|
|
22
|
+
attribute :scrolling, :boolean, default: false
|
|
23
|
+
attribute :inline, :boolean, default: false
|
|
24
|
+
attribute :floating, :boolean, default: false
|
|
25
|
+
attribute :button, :boolean, default: false
|
|
26
|
+
attribute :labeled, :boolean, default: false
|
|
27
|
+
attribute :loading, :boolean, default: false
|
|
28
|
+
attribute :disabled, :boolean, default: false
|
|
29
|
+
|
|
30
|
+
def to_s
|
|
31
|
+
classes = class_names(
|
|
32
|
+
"ui",
|
|
33
|
+
pointing && "#{pointing} pointing",
|
|
34
|
+
{ "selection" => selection, "search" => search, "multiple" => multiple,
|
|
35
|
+
"clearable" => clearable, "fluid" => fluid, "compact" => compact,
|
|
36
|
+
"scrolling" => scrolling, "inline" => inline, "floating" => floating,
|
|
37
|
+
"button" => button, "labeled" => labeled, "loading" => loading,
|
|
38
|
+
"disabled" => disabled },
|
|
39
|
+
"dropdown"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
data = { controller: "fui-dropdown" }
|
|
43
|
+
data[:fui_dropdown_clearable_value] = "true" if clearable
|
|
44
|
+
data[:fui_dropdown_placeholder_value] = placeholder if placeholder
|
|
45
|
+
|
|
46
|
+
hidden_opts = { type: "hidden", value: default_value || "" }
|
|
47
|
+
hidden_opts[:name] = name if name
|
|
48
|
+
|
|
49
|
+
search_el = search ? tag.input(class: "search") : nil
|
|
50
|
+
text_el = tag.div(class: "default text") { placeholder || "" }
|
|
51
|
+
menu_el = tag.div(class: "menu") { @content }
|
|
52
|
+
|
|
53
|
+
tag.div(class: classes, data: data) {
|
|
54
|
+
safe_join([
|
|
55
|
+
tag.input(**hidden_opts),
|
|
56
|
+
search_el,
|
|
57
|
+
text_el,
|
|
58
|
+
tag.i(class: "dropdown icon"),
|
|
59
|
+
menu_el
|
|
60
|
+
])
|
|
61
|
+
}
|
|
62
|
+
end
|
|
63
|
+
end
|