pulse_view_components 0.3.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/CHANGELOG.md +88 -0
- data/LICENSE.txt +22 -0
- data/README.md +78 -0
- data/app/assets/builds/js/pulse_view_components.js +9543 -0
- data/app/assets/builds/js/pulse_view_components.js.map +7 -0
- data/app/assets/builds/pulse_view_components.css +1 -0
- data/app/assets/config/manifest.js +2 -0
- data/app/assets/images/pulse_view_components/icons/alert.svg +3 -0
- data/app/assets/images/pulse_view_components/icons/archive.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/arrow-both.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/arrow-down.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/arrow-left.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/arrow-right.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/arrow-switch.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/arrow-up.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/beaker.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/bell-slash.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/bell.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/bookmark-slash.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/bookmark.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/calendar.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/check-circle.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/check.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/chevron-down.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/circle.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/clock.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/close.svg +9 -0
- data/app/assets/images/pulse_view_components/icons/cloud.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/comment-discussion.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/comment.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/copy.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/database.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/dot.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/dots-outline.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/download.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/duplicate.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/eye.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/file-added.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/file-directory.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/file-media.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/file.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/filter.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/flame.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/gear.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/gift.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/globe.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/goal.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/graph.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/heart-fill.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/heart.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/history.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/home.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/hourglass.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/image.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/inbox.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/info.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/issue.svg +3 -0
- data/app/assets/images/pulse_view_components/icons/key.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/law.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/link-external.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/link.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/location.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/log.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/mail.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/mark-github.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/mark-jira.svg +3 -0
- data/app/assets/images/pulse_view_components/icons/megaphone.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/no-entry.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/note.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/organization.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/package.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/paintbrush.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/paperclip.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/paste.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/pencil.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/people.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/person-add.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/person.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/pin-slash.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/pin.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/play.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/plus-circle.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/plus.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/project.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/pulse.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/question.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/redo.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/reply.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/report.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/rss.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/ruby.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/search.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/share.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/shield.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/sliders.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/smiley.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/sort.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/sort_asc.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/sort_desc.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/stack.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/star-fill.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/star.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/stopwatch.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/sun.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/sync.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/table.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/tag.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/tasklist.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/telescope.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/three-bars.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/thumbsdown.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/thumbsup.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/tools.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/trash.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/trophy.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/undo.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/unlink.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/upload.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/x-circle.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/x.svg +1 -0
- data/app/assets/images/pulse_view_components/icons/zap.svg +1 -0
- data/app/assets/javascripts/pulse_view_components.js +0 -0
- data/app/assets/stylesheets/pulse_view_components/application.tailwind.css +10 -0
- data/app/components/pulse/action_list/divider.rb +35 -0
- data/app/components/pulse/action_list/form_wrapper.html.slim +9 -0
- data/app/components/pulse/action_list/form_wrapper.rb +63 -0
- data/app/components/pulse/action_list/heading.rb +52 -0
- data/app/components/pulse/action_list/item.html.slim +43 -0
- data/app/components/pulse/action_list/item.rb +319 -0
- data/app/components/pulse/action_list.css +568 -0
- data/app/components/pulse/action_list.rb +261 -0
- data/app/components/pulse/badge.rb +59 -0
- data/app/components/pulse/base_component.rb +36 -0
- data/app/components/pulse/blank_slate.html.slim +10 -0
- data/app/components/pulse/blank_slate.rb +19 -0
- data/app/components/pulse/blank_slate.yml +2 -0
- data/app/components/pulse/border_box/header.html.slim +4 -0
- data/app/components/pulse/border_box/header.rb +48 -0
- data/app/components/pulse/border_box.html.slim +10 -0
- data/app/components/pulse/border_box.rb +124 -0
- data/app/components/pulse/box.rb +17 -0
- data/app/components/pulse/breadcrumb_item.rb +40 -0
- data/app/components/pulse/breadcrumbs.html.slim +9 -0
- data/app/components/pulse/breadcrumbs.rb +17 -0
- data/app/components/pulse/button.rb +148 -0
- data/app/components/pulse/component.rb +99 -0
- data/app/components/pulse/conditional_wrapper.rb +21 -0
- data/app/components/pulse/content.rb +11 -0
- data/app/components/pulse/counter.css +10 -0
- data/app/components/pulse/counter.rb +107 -0
- data/app/components/pulse/flash.html.slim +9 -0
- data/app/components/pulse/flash.rb +52 -0
- data/app/components/pulse/forms/error_explanation.html.slim +8 -0
- data/app/components/pulse/forms/error_explanation.rb +36 -0
- data/app/components/pulse/icon.rb +30 -0
- data/app/components/pulse/label.html.slim +5 -0
- data/app/components/pulse/label.rb +42 -0
- data/app/components/pulse/layout.css +239 -0
- data/app/components/pulse/layout.html.slim +7 -0
- data/app/components/pulse/layout.rb +141 -0
- data/app/components/pulse/link.html.slim +7 -0
- data/app/components/pulse/link.rb +52 -0
- data/app/components/pulse/modal.html.slim +51 -0
- data/app/components/pulse/modal.rb +64 -0
- data/app/components/pulse/modal_controller.js +77 -0
- data/app/components/pulse/nav_link.html.slim +16 -0
- data/app/components/pulse/nav_link.rb +25 -0
- data/app/components/pulse/nav_list/divider.rb +13 -0
- data/app/components/pulse/nav_list/group.html.slim +6 -0
- data/app/components/pulse/nav_list/group.rb +110 -0
- data/app/components/pulse/nav_list/heading.rb +36 -0
- data/app/components/pulse/nav_list/item.rb +171 -0
- data/app/components/pulse/nav_list.html.slim +11 -0
- data/app/components/pulse/nav_list.rb +225 -0
- data/app/components/pulse/popover.html.slim +5 -0
- data/app/components/pulse/popover.rb +88 -0
- data/app/components/pulse/progress_bar.html.slim +5 -0
- data/app/components/pulse/progress_bar.rb +23 -0
- data/app/components/pulse/progress_bar.yml +2 -0
- data/app/components/pulse/table/cell.html.slim +10 -0
- data/app/components/pulse/table/cell.rb +90 -0
- data/app/components/pulse/table/cell.yml +5 -0
- data/app/components/pulse/table/column.rb +22 -0
- data/app/components/pulse/table.html.slim +50 -0
- data/app/components/pulse/table.rb +111 -0
- data/app/components/pulse/table.yml +2 -0
- data/app/components/pulse/text.rb +72 -0
- data/app/components/pulse/toast.html.slim +14 -0
- data/app/components/pulse/toast.rb +85 -0
- data/app/components/pulse/toast_controller.js +32 -0
- data/app/javascript/pulse_view_components/application.js +24 -0
- data/app/javascript/pulse_view_components.js +4 -0
- data/app/lib/pulse/attributes_helper.rb +107 -0
- data/app/lib/pulse/fetch_or_fallback_helper.rb +53 -0
- data/app/lib/pulse/svg_helper.rb +28 -0
- data/lib/pulse/view_components/engine.rb +74 -0
- data/lib/pulse/view_components/version.rb +17 -0
- data/lib/pulse/view_components.rb +14 -0
- data/lib/pulse_view_components.rb +3 -0
- data/lib/tasks/pulse_view_components_tasks.rake +8 -0
- data/previews/pulse/badge_preview.rb +51 -0
- data/previews/pulse/blank_slate_preview.rb +14 -0
- data/previews/pulse/border_box_preview.rb +48 -0
- data/previews/pulse/breadcrumbs_preview.rb +13 -0
- data/previews/pulse/button_preview.rb +137 -0
- data/previews/pulse/counter_preview.rb +14 -0
- data/previews/pulse/flash_preview.rb +85 -0
- data/previews/pulse/icon_preview.rb +12 -0
- data/previews/pulse/label_preview.rb +10 -0
- data/previews/pulse/layout_preview.rb +34 -0
- data/previews/pulse/link_preview.rb +55 -0
- data/previews/pulse/modal_preview.rb +48 -0
- data/previews/pulse/nav_link_preview.rb +48 -0
- data/previews/pulse/nav_list_preview.rb +138 -0
- data/previews/pulse/popover_preview.rb +77 -0
- data/previews/pulse/progress_bar_preview.rb +23 -0
- data/previews/pulse/table_preview.rb +168 -0
- data/previews/pulse/text_preview.rb +102 -0
- data/previews/pulse/toast_preview.rb +67 -0
- metadata +344 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pulse
|
|
4
|
+
class NavList
|
|
5
|
+
# A logical grouping of navigation links with an optional heading.
|
|
6
|
+
#
|
|
7
|
+
# See <%= link_to_component(Pulse::NavList) %> for usage examples.
|
|
8
|
+
class Group < ActionList
|
|
9
|
+
# A special "show more" list item that appears at the bottom of the group. Clicking
|
|
10
|
+
# the item will fetch the next page of results from the URL passed in the `src` argument
|
|
11
|
+
# and append the resulting chunk of HTML to the group.
|
|
12
|
+
#
|
|
13
|
+
# @param src [String] The URL to query for additional pages of list items.
|
|
14
|
+
# @param pages [Integer] The total number of pages in the result set.
|
|
15
|
+
# @param component_klass [Class] A component class to use instead of the default `Pulse::NavList::Item` class.
|
|
16
|
+
# @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Pulse::NavList::Item) %>.
|
|
17
|
+
renders_one :show_more_item, lambda { |src:, pages:,
|
|
18
|
+
component_klass: NavList::Item,
|
|
19
|
+
**system_arguments|
|
|
20
|
+
system_arguments[:classes] = merge_classes(
|
|
21
|
+
@item_classes,
|
|
22
|
+
system_arguments[:classes]
|
|
23
|
+
)
|
|
24
|
+
system_arguments[:tag] = :div
|
|
25
|
+
system_arguments[:id] ||= self.class.generate_id(base_name: 'item')
|
|
26
|
+
system_arguments[:hidden] = true
|
|
27
|
+
system_arguments[:href] = '#'
|
|
28
|
+
system_arguments[:data] ||= {}
|
|
29
|
+
system_arguments[:data][:target] = 'nav-list.showMoreItem'
|
|
30
|
+
system_arguments[:data][:action] = 'click:nav-list#showMore'
|
|
31
|
+
system_arguments[:data][:current_page] = '1'
|
|
32
|
+
system_arguments[:data][:total_pages] = pages.to_s
|
|
33
|
+
system_arguments[:label_arguments] = {
|
|
34
|
+
**system_arguments[:label_arguments] || {},
|
|
35
|
+
color: :accent
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
system_arguments[:content_arguments] = {
|
|
39
|
+
**system_arguments[:content_arguments] || {},
|
|
40
|
+
tag: :button
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
system_arguments[:content_arguments][:data] = merge_data(
|
|
44
|
+
system_arguments[:content_arguments],
|
|
45
|
+
data: { list_id: id }
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
component_klass.new(list: self, src:, **system_arguments)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
# @private
|
|
52
|
+
def self.custom_element_name
|
|
53
|
+
Pulse::NavList.custom_element_name
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# @param selected_item_id [Symbol] The ID of the currently selected item. Used internally.
|
|
57
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
|
58
|
+
def initialize(selected_item_id: nil, **system_arguments)
|
|
59
|
+
@system_arguments = system_arguments
|
|
60
|
+
@selected_item_id = selected_item_id
|
|
61
|
+
|
|
62
|
+
super(**@system_arguments)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Cause this group to show its list of sub items when rendered.
|
|
66
|
+
# simplecov:disable
|
|
67
|
+
def expand!
|
|
68
|
+
@expanded = true
|
|
69
|
+
end
|
|
70
|
+
# simplecov:enable
|
|
71
|
+
|
|
72
|
+
# @!parse
|
|
73
|
+
# # Items.
|
|
74
|
+
# #
|
|
75
|
+
# # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Pulse::NavList::Item) %>.
|
|
76
|
+
# renders_many :items
|
|
77
|
+
|
|
78
|
+
# @private
|
|
79
|
+
def build_item(component_klass: NavList::Item, **system_arguments)
|
|
80
|
+
super(
|
|
81
|
+
component_klass:,
|
|
82
|
+
selected_item_id: @selected_item_id,
|
|
83
|
+
**system_arguments
|
|
84
|
+
)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# @private
|
|
88
|
+
def build_avatar_item(component_klass: NavList::Item, **system_arguments)
|
|
89
|
+
super(
|
|
90
|
+
component_klass:,
|
|
91
|
+
selected_item_id: @selected_item_id,
|
|
92
|
+
**system_arguments
|
|
93
|
+
)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def kind
|
|
97
|
+
:group
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def before_render
|
|
101
|
+
super
|
|
102
|
+
|
|
103
|
+
return if heading?
|
|
104
|
+
|
|
105
|
+
raise ArgumentError,
|
|
106
|
+
'NavList groups are required to have headings'
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pulse
|
|
4
|
+
class NavList
|
|
5
|
+
# The heading placed above a `NavList`'s items.
|
|
6
|
+
#
|
|
7
|
+
# See <%= link_to_component(NavList) %> for usage examples.
|
|
8
|
+
class Heading < Pulse::Component
|
|
9
|
+
attr_reader :title, :id, :heading_level, :system_arguments
|
|
10
|
+
|
|
11
|
+
# @param title [String] The text content of the heading.
|
|
12
|
+
# @param id [String] The value of the ID HTML attribute. Auto-generated by default.
|
|
13
|
+
# @param heading_level [Integer] The heading level, i.e. 2 for an `<h2>`, 3 for an `<h3>`, etc.
|
|
14
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
|
15
|
+
def initialize(title:, id: self.class.generate_id, heading_level: 2,
|
|
16
|
+
**system_arguments)
|
|
17
|
+
@title = title
|
|
18
|
+
@id = id
|
|
19
|
+
@heading_level = heading_level
|
|
20
|
+
@system_arguments = system_arguments
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def call
|
|
24
|
+
render(
|
|
25
|
+
Pulse::Text.new(
|
|
26
|
+
type: :body4,
|
|
27
|
+
tag: :"h#{heading_level}",
|
|
28
|
+
id:,
|
|
29
|
+
classes: 'pulse-ml-2 pulse-mb-4',
|
|
30
|
+
**system_arguments
|
|
31
|
+
).with_content(title)
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pulse
|
|
4
|
+
class NavList
|
|
5
|
+
# Items are rendered as styled links. They can optionally include leading and/or trailing visuals,
|
|
6
|
+
# such as icons, avatars, and counters. Items are selected by ID. IDs can be specified via the
|
|
7
|
+
# `selected_item_ids` argument, which accepts a list of valid IDs for the item. Items can also
|
|
8
|
+
# themselves contain sub items. Sub items are rendered collapsed by default.
|
|
9
|
+
class Item < Pulse::ActionList::Item
|
|
10
|
+
attr_reader :selected_by_ids, :sub_item
|
|
11
|
+
|
|
12
|
+
# @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Pulse::ActionList::Item) %>.
|
|
13
|
+
renders_many :items, lambda { |**system_arguments|
|
|
14
|
+
raise 'Items can only be nested 2 levels deep' if sub_item?
|
|
15
|
+
|
|
16
|
+
@list.build_item(parent: self,
|
|
17
|
+
sub_item: true,
|
|
18
|
+
**system_arguments).tap do |item|
|
|
19
|
+
@list.will_add_item(item)
|
|
20
|
+
end
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
# Whether or not this item is nested under a parent item.
|
|
24
|
+
#
|
|
25
|
+
# @return [Boolean]
|
|
26
|
+
alias sub_item? sub_item
|
|
27
|
+
|
|
28
|
+
# @param selected_item_id [Symbol] The ID of the currently selected list item. Used internally.
|
|
29
|
+
# @param selected_by_ids [Array<Symbol>] The list of IDs that select this item. In other words, if the `selected_item_id` attribute on the parent `NavList` is set to one of these IDs, the item will appear selected.
|
|
30
|
+
# @param expanded [Boolean] Whether this item shows (expands) or hides (collapses) its list of sub items.
|
|
31
|
+
# @param sub_item [Boolean] Whether or not this item is nested under a parent item. Used internally.
|
|
32
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
|
33
|
+
def initialize(selected_item_id: nil, selected_by_ids: [],
|
|
34
|
+
sub_item: false, expanded: false, **system_arguments)
|
|
35
|
+
@selected_item_id = selected_item_id
|
|
36
|
+
@selected_by_ids = Array(selected_by_ids)
|
|
37
|
+
@expanded = expanded
|
|
38
|
+
@sub_item = sub_item
|
|
39
|
+
|
|
40
|
+
system_arguments[:classes] = merge_classes(
|
|
41
|
+
system_arguments[:classes],
|
|
42
|
+
'ActionListItem--subItem' => @sub_item
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
@sub_list_arguments = {
|
|
46
|
+
classes: merge_classes(
|
|
47
|
+
'ActionList',
|
|
48
|
+
'ActionList--subGroup'
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@list = system_arguments[:list]
|
|
53
|
+
|
|
54
|
+
if @list
|
|
55
|
+
@sub_list_arguments['data-action'] =
|
|
56
|
+
"keydown:#{@list.custom_element_name}#handleItemWithSubItemKeydown"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
overrides = { 'data-item-id': @selected_by_ids.join(' ') }
|
|
60
|
+
|
|
61
|
+
super(**system_arguments, **overrides)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def active?
|
|
65
|
+
item_active?(self) && items.empty?
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Cause this item to show its list of sub items when rendered.
|
|
69
|
+
def expand!
|
|
70
|
+
@expanded = true
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def before_render
|
|
74
|
+
if active_sub_item?
|
|
75
|
+
expand!
|
|
76
|
+
|
|
77
|
+
@content_arguments[:classes] = merge_classes(
|
|
78
|
+
@content_arguments[:classes],
|
|
79
|
+
'ActionListContent--hasActiveSubItem'
|
|
80
|
+
)
|
|
81
|
+
else
|
|
82
|
+
@system_arguments[:classes] = merge_classes(
|
|
83
|
+
@system_arguments[:classes],
|
|
84
|
+
'pulse-outline-none pulse-bg-gray-50' => active?
|
|
85
|
+
)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
@content_arguments[:'aria-current'] = 'page' if active?
|
|
89
|
+
|
|
90
|
+
super
|
|
91
|
+
|
|
92
|
+
if items.present? && trailing_action.present?
|
|
93
|
+
raise 'Cannot render a trailing action for an item with subitems'
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
if items.present? && @selected_by_ids.present?
|
|
97
|
+
raise 'Cannot pass `selected_by_ids:` for an item with subitems, ' \
|
|
98
|
+
'since parent items cannot be selected'
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
return if items.blank?
|
|
102
|
+
|
|
103
|
+
@sub_list_arguments[:aria] = merge_aria(
|
|
104
|
+
@sub_list_arguments,
|
|
105
|
+
{ aria: { labelledby: id } }
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
if href.present?
|
|
109
|
+
raise ArgumentError,
|
|
110
|
+
'Items with sub-items cannot have hrefs'
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
@content_arguments[:tag] = :button
|
|
114
|
+
@content_arguments[:'aria-expanded'] = @expanded.to_s
|
|
115
|
+
@content_arguments[:'data-action'] = "
|
|
116
|
+
click:#{@list.custom_element_name}#handleItemWithSubItemClick
|
|
117
|
+
keydown:#{@list.custom_element_name}#handleItemWithSubItemKeydown
|
|
118
|
+
"
|
|
119
|
+
|
|
120
|
+
with_private_trailing_action_icon(
|
|
121
|
+
:'chevron-down',
|
|
122
|
+
classes: 'ActionListItem-collapseIcon'
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
@system_arguments[:classes] = merge_classes(
|
|
126
|
+
@system_arguments[:classes],
|
|
127
|
+
'ActionListItem--hasSubItem'
|
|
128
|
+
)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def kind
|
|
132
|
+
:item
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
private
|
|
136
|
+
|
|
137
|
+
# Normally it would be easier to simply ask each item for its active status, eg.
|
|
138
|
+
# items.any?(&:active?), but unfortunately the view context is not set on each
|
|
139
|
+
# item until _after_ the parent's before_render, etc methods have been called.
|
|
140
|
+
# This means helper methods like current_page? will blow up with an error, since
|
|
141
|
+
# `helpers` is simply an alias for the view context (i.e. an instance of
|
|
142
|
+
# ActionView::Base). Since we know the view context for the parent object must
|
|
143
|
+
# be set before `before_render` is invoked, we can call helper methods here in
|
|
144
|
+
# the parent and bypass the problem entirely. Maybe not the most OO approach,
|
|
145
|
+
# but it works.
|
|
146
|
+
def item_active?(item)
|
|
147
|
+
if item.selected_by_ids.present?
|
|
148
|
+
item.selected_by_ids.include?(@selected_item_id)
|
|
149
|
+
elsif item.href
|
|
150
|
+
current_page?(item.href)
|
|
151
|
+
else
|
|
152
|
+
# simplecov:disable
|
|
153
|
+
false
|
|
154
|
+
# simplecov:enable
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def active_sub_item?
|
|
159
|
+
items.any? { |subitem| item_active?(subitem) }
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def current_page?(url)
|
|
163
|
+
helpers.current_page?(url)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def list_class
|
|
167
|
+
Pulse::NavList
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/! components/pulse/nav_list
|
|
2
|
+
= render Pulse::BaseComponent.new(tag: :nav, **@system_arguments) do
|
|
3
|
+
= heading
|
|
4
|
+
nav-list
|
|
5
|
+
= render(Pulse::ConditionalWrapper.new(condition: render_outer_list?, tag: :ul)) do
|
|
6
|
+
- items.each_with_index do |item, index|
|
|
7
|
+
- if index > 0 && render_divider_between?(item, items[index - 1])
|
|
8
|
+
= render(Pulse::ActionList::Divider.new)
|
|
9
|
+
|
|
10
|
+
= render(Pulse::ConditionalWrapper.new(condition: render_outer_list? && group?(item), tag: :li)) do
|
|
11
|
+
= item
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pulse
|
|
4
|
+
# `NavList` provides a simple way to render side navigation, i.e. navigation
|
|
5
|
+
# that appears to the left or right side of some main content. Each group in a
|
|
6
|
+
# nav list is a list of links.
|
|
7
|
+
#
|
|
8
|
+
# Nav list groups can contain sub items. Rather than navigating to a URL, groups
|
|
9
|
+
# with sub items expand and collapse on click. To indicate this functionality, the
|
|
10
|
+
# group will automatically render with a trailing chevron icon that changes direction
|
|
11
|
+
# when the group expands and collapses.
|
|
12
|
+
#
|
|
13
|
+
# Nav list items appear visually active when selected. Each nav item must have one
|
|
14
|
+
# or more ID values that determine which item will appear selected. Use the
|
|
15
|
+
# `selected_item_id` argument to select the appropriate item.
|
|
16
|
+
class NavList < Pulse::Component
|
|
17
|
+
# @private
|
|
18
|
+
def self.custom_element_name
|
|
19
|
+
'nav-list'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# The heading for the list at large. Accepts the arguments accepted by <%= link_to_component(Pulse::NavList::Heading) %>.
|
|
23
|
+
#
|
|
24
|
+
renders_one :heading, Pulse::NavList::Heading
|
|
25
|
+
|
|
26
|
+
# @!parse
|
|
27
|
+
# # Adds an item to the list.
|
|
28
|
+
# #
|
|
29
|
+
# # @param component_klass [Class] The class to use instead of the default <%= link_to_component(Pulse::NavList::Item) %>
|
|
30
|
+
# # @param system_arguments [Hash] These arguments are forwarded to <%= link_to_component(Pulse::NavList::Item) %>, or whatever class is passed as the `component_klass` argument.
|
|
31
|
+
# def with_item(component_klass: Pulse::NavList::Item, **system_arguments, &block)
|
|
32
|
+
# end
|
|
33
|
+
|
|
34
|
+
# @!parse
|
|
35
|
+
# # Adds an avatar item to the list. Avatar items are a convenient way to accessibly add an item with a leading avatar image.
|
|
36
|
+
# #
|
|
37
|
+
# # @param src [String] The source url of the avatar image.
|
|
38
|
+
# # @param username [String] The username associated with the avatar.
|
|
39
|
+
# # @param full_name [String] Optional. The user's full name.
|
|
40
|
+
# # @param full_name_scheme [Symbol] Optional. How to display the user's full name. <%= one_of(ActionList::Item::DESCRIPTION_SCHEME_OPTIONS) %>
|
|
41
|
+
# # @param component_klass [Class] The class to use instead of the default <%= link_to_component(Pulse::NavList::Item) %>
|
|
42
|
+
# # @param avatar_arguments [Hash] Optional. The arguments accepted by <%= link_to_component(Pulse::Avatar) %>
|
|
43
|
+
# # @param system_arguments [Hash] These arguments are forwarded to <%= link_to_component(Pulse::NavList::Item) %>, or whatever class is passed as the `component_klass` argument.
|
|
44
|
+
# def with_avatar_item(src:, username:, full_name: nil, full_name_scheme: ActionList::Item::DEFAULT_DESCRIPTION_SCHEME, component_klass: Pulse::NavList::Item, avatar_arguments: {}, **system_arguments, &block)
|
|
45
|
+
# end
|
|
46
|
+
|
|
47
|
+
# @!parse
|
|
48
|
+
# # Adds a group to the list. A group is a list of links and a (required) heading.
|
|
49
|
+
# #
|
|
50
|
+
# # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Pulse::NavList::Group) %>.
|
|
51
|
+
# def with_group(**system_arguments, &block)
|
|
52
|
+
# end
|
|
53
|
+
|
|
54
|
+
# @!parse
|
|
55
|
+
# # Adds a divider to the list. Dividers visually separate items and groups.
|
|
56
|
+
# #
|
|
57
|
+
# # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Pulse::NavList::Divider) %>.
|
|
58
|
+
# def with_divider(**system_arguments, &block)
|
|
59
|
+
# end
|
|
60
|
+
|
|
61
|
+
# Items. Items can be individual items, dividers, or groups. See the documentation for `#with_item`, `#with_divider`, and `#with_group` respectively for more information.
|
|
62
|
+
#
|
|
63
|
+
renders_many :items, types: {
|
|
64
|
+
item: {
|
|
65
|
+
renders: lambda { |**system_arguments, &block|
|
|
66
|
+
build_item(**system_arguments, &block)
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
as: :item
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
avatar_item: {
|
|
73
|
+
renders: lambda { |**system_arguments|
|
|
74
|
+
build_avatar_item(**system_arguments)
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
as: :avatar_item
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
divider: {
|
|
81
|
+
renders: Pulse::NavList::Divider,
|
|
82
|
+
as: :divider
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
group: {
|
|
86
|
+
renders: lambda { |**system_arguments, &block|
|
|
87
|
+
Pulse::NavList::Group.new(
|
|
88
|
+
selected_item_id: @selected_item_id,
|
|
89
|
+
**system_arguments,
|
|
90
|
+
&block
|
|
91
|
+
)
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
as: :group
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
# @param selected_item_id [Symbol] The ID of the currently selected item. The default is `nil`, meaning no item is selected.
|
|
99
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
|
100
|
+
def initialize(selected_item_id: nil, **system_arguments)
|
|
101
|
+
@system_arguments = system_arguments
|
|
102
|
+
@selected_item_id = selected_item_id
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Builds a new item but does not add it to the list. Use this method
|
|
106
|
+
# instead of the `#with_item` slot if you need to render an item outside
|
|
107
|
+
# the context of a list, eg. if rendering additional items to append to
|
|
108
|
+
# an existing list, perhaps via a separate HTTP request.
|
|
109
|
+
#
|
|
110
|
+
# @param component_klass [Class] The class to use instead of the default <%= link_to_component(Pulse::NavList::Item) %>
|
|
111
|
+
# @param system_arguments [Hash] These arguments are forwarded to <%= link_to_component(Pulse::NavList::Item) %>, or whatever class is passed as the `component_klass` argument.
|
|
112
|
+
def build_item(component_klass: Pulse::NavList::Item, **system_arguments,
|
|
113
|
+
&)
|
|
114
|
+
component_klass.new(
|
|
115
|
+
list: top_level_group,
|
|
116
|
+
selected_item_id: @selected_item_id,
|
|
117
|
+
**system_arguments,
|
|
118
|
+
&
|
|
119
|
+
)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Builds a new avatar item but does not add it to the list. Avatar items
|
|
123
|
+
# are a convenient way to accessibly add an item with a leading avatar
|
|
124
|
+
# image. Use this method instead of the `#with_avatar_item` slot if you
|
|
125
|
+
# need to render an avatar item outside the context of a list, eg. if
|
|
126
|
+
# rendering additional items to append to an existing list, perhaps via
|
|
127
|
+
# a separate HTTP request.
|
|
128
|
+
#
|
|
129
|
+
# @param src [String] The source url of the avatar image.
|
|
130
|
+
# @param username [String] The username associated with the avatar.
|
|
131
|
+
# @param full_name [String] Optional. The user's full name.
|
|
132
|
+
# @param full_name_scheme [Symbol] Optional. How to display the user's full name. <%= one_of(ActionList::ItemComponent::DESCRIPTION_SCHEME_OPTIONS) %>
|
|
133
|
+
# @param component_klass [Class] The class to use instead of the default <%= link_to_component(Pulse::NavList::Item) %>
|
|
134
|
+
# @param avatar_arguments [Hash] Optional. The arguments accepted by <%= link_to_component(Pulse::Avatar) %>
|
|
135
|
+
# @param system_arguments [Hash] These arguments are forwarded to <%= link_to_component(Pulse::NavList::Item) %>, or whatever class is passed as the `component_klass` argument.
|
|
136
|
+
def build_avatar_item(
|
|
137
|
+
src:, username:, full_name: nil,
|
|
138
|
+
full_name_scheme: ActionList::Item::DEFAULT_DESCRIPTION_SCHEME,
|
|
139
|
+
component_klass: Pulse::NavList::Item, avatar_arguments: {},
|
|
140
|
+
**system_arguments
|
|
141
|
+
)
|
|
142
|
+
component_klass.new(
|
|
143
|
+
list: top_level_group,
|
|
144
|
+
selected_item_id: @selected_item_id,
|
|
145
|
+
label: username,
|
|
146
|
+
description_scheme: full_name_scheme,
|
|
147
|
+
**system_arguments
|
|
148
|
+
).tap do |item|
|
|
149
|
+
item.with_leading_visual_raw_content do
|
|
150
|
+
# no alt text necessary
|
|
151
|
+
item.render(Pulse::Avatar.new(src:, **avatar_arguments,
|
|
152
|
+
role: :presentation, size: 16))
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
item.with_description_content(full_name) if full_name
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
private
|
|
160
|
+
|
|
161
|
+
def before_render
|
|
162
|
+
if heading?
|
|
163
|
+
if aria(
|
|
164
|
+
:label, @system_arguments
|
|
165
|
+
)
|
|
166
|
+
raise ArgumentError,
|
|
167
|
+
"Please don't set an aria-label if a heading is provided"
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
@system_arguments[:aria] = merge_aria(
|
|
171
|
+
@system_arguments,
|
|
172
|
+
{ aria: { labelledby: heading.id } }
|
|
173
|
+
)
|
|
174
|
+
else
|
|
175
|
+
unless aria(
|
|
176
|
+
:label, @system_arguments
|
|
177
|
+
)
|
|
178
|
+
raise ArgumentError,
|
|
179
|
+
'When no heading is provided, an aria-label must be given'
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Lists that contain top-level items (i.e. items outside of a group) should be wrapped in a <ul>
|
|
185
|
+
def render_outer_list?
|
|
186
|
+
items.any? { |item| !group?(item) }
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def render_divider_between?(item1, item2)
|
|
190
|
+
return false if either_is_divider?(item1, item2)
|
|
191
|
+
|
|
192
|
+
both_are_groups?(item1, item2) || heterogeneous?(item1, item2)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def both_are_groups?(item1, item2)
|
|
196
|
+
group?(item1) && group?(item2)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def heterogeneous?(item1, item2)
|
|
200
|
+
kind(item1) != kind(item2)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def either_is_divider?(item1, item2)
|
|
204
|
+
divider?(item1) || divider?(item2)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def group?(item)
|
|
208
|
+
kind(item) == :group
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def divider?(item)
|
|
212
|
+
kind(item) == :divider
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def kind(item)
|
|
216
|
+
item.respond_to?(:kind) ? item.kind : :item
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def top_level_group
|
|
220
|
+
# dummy group for the list: argument in the item slot above
|
|
221
|
+
@top_level_group ||=
|
|
222
|
+
Pulse::NavList::Group.new(selected_item_id: @selected_item_id)
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pulse
|
|
4
|
+
# Use `Popover` to bring attention to specific user interface elements,
|
|
5
|
+
# typically to suggest an action or to guide users through a new experience.
|
|
6
|
+
#
|
|
7
|
+
# By default, the popover renders with absolute positioning, meaning it should
|
|
8
|
+
# usually be wrapped in an element with a relative position in order to be
|
|
9
|
+
# positioned properly. To render the popover with relative positioning, use
|
|
10
|
+
# the relative property.
|
|
11
|
+
class Popover < Pulse::Component
|
|
12
|
+
CARET_DEFAULT = :top
|
|
13
|
+
CARET_MAPPINGS = {
|
|
14
|
+
CARET_DEFAULT => 'pulse-caret-top',
|
|
15
|
+
:bottom => 'pulse-caret-bottom',
|
|
16
|
+
:bottom_right => 'Popover-message--bottom-right',
|
|
17
|
+
:bottom_left => 'Popover-message--bottom-left',
|
|
18
|
+
:left => 'pulse-caret-left',
|
|
19
|
+
:left_bottom => 'Popover-message--left-bottom',
|
|
20
|
+
:left_top => 'Popover-message--left-top',
|
|
21
|
+
:right => 'pulse-caret-right',
|
|
22
|
+
:right_bottom => 'Popover-message--right-bottom',
|
|
23
|
+
:right_top => 'Popover-message--right-top',
|
|
24
|
+
:top_left => 'Popover-message--top-left',
|
|
25
|
+
:top_right => 'Popover-message--top-right'
|
|
26
|
+
}.freeze
|
|
27
|
+
|
|
28
|
+
DEFAULT_HEADING_TAG = :h4
|
|
29
|
+
|
|
30
|
+
# The heading
|
|
31
|
+
#
|
|
32
|
+
# @param tag [Symbol] (Pulse::Popover::DEFAULT_HEADING_TAG) <%= one_of(Pulse::Heading::TAG_OPTIONS) %>
|
|
33
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
|
34
|
+
renders_one :heading, lambda { |tag: DEFAULT_HEADING_TAG,
|
|
35
|
+
**system_arguments|
|
|
36
|
+
system_arguments[:type] = :body3
|
|
37
|
+
system_arguments[:tag] = tag
|
|
38
|
+
system_arguments[:classes] = merge_classes(
|
|
39
|
+
'pulse-mb-2',
|
|
40
|
+
system_arguments[:classes]
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
Pulse::Text.new(**system_arguments)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# The body
|
|
47
|
+
#
|
|
48
|
+
# @param caret [Symbol] <%= one_of(Pulse::Popover::CARET_MAPPINGS.keys) %>
|
|
49
|
+
# @param large [Boolean] Whether to use the large version of the component.
|
|
50
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
|
51
|
+
renders_one :body, lambda { |caret: CARET_DEFAULT, large: false,
|
|
52
|
+
**system_arguments|
|
|
53
|
+
@body_arguments = system_arguments
|
|
54
|
+
@body_arguments['data-caret'] = caret
|
|
55
|
+
@body_arguments[:classes] = merge_classes(
|
|
56
|
+
@body_arguments[:classes],
|
|
57
|
+
CARET_MAPPINGS[fetch_or_fallback(CARET_MAPPINGS.keys, caret,
|
|
58
|
+
CARET_DEFAULT)],
|
|
59
|
+
'Popover-message--large' => large
|
|
60
|
+
)
|
|
61
|
+
@body_arguments[:classes] = merge_classes(
|
|
62
|
+
@body_arguments[:classes],
|
|
63
|
+
'pulse-bg-white pulse-border pulse-border-gray-300 pulse-rounded-lg',
|
|
64
|
+
'pulse-p-4 pulse-mt-2 pulse-mx-auto pulse-text-left pulse-shadow-lg'
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
Pulse::Content.new
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
|
71
|
+
def initialize(**system_arguments)
|
|
72
|
+
@system_arguments = deny_tag_argument(**system_arguments)
|
|
73
|
+
@system_arguments[:tag] = :div
|
|
74
|
+
@system_arguments[:classes] = merge_classes(
|
|
75
|
+
'pulse-relative pulse-z-10 pulse-w-64',
|
|
76
|
+
system_arguments[:classes]
|
|
77
|
+
)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def render?
|
|
81
|
+
body.present?
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def body_component
|
|
85
|
+
Pulse::Box.new(**@body_arguments)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/! components/pulse/progress_bar
|
|
2
|
+
= render Pulse::BaseComponent.new(**@system_attributes) do
|
|
3
|
+
div class="pulse-h-1 pulse-bg-success" style="width: #{[@percentage, 100].min}%"
|
|
4
|
+
- if @percentage > 100
|
|
5
|
+
div class="pulse-h-1 pulse-bg-error" style="width: #{@percentage - 100}%"
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pulse
|
|
4
|
+
# Renders a simple progress bar
|
|
5
|
+
class ProgressBar < Pulse::Component
|
|
6
|
+
def initialize(percentage: 0, **system_attributes)
|
|
7
|
+
@percentage = percentage.to_i
|
|
8
|
+
@system_attributes = system_attributes
|
|
9
|
+
@system_attributes[:tag] ||= :div
|
|
10
|
+
@system_attributes[:class] = merge_classes(
|
|
11
|
+
'pulse-flex pulse-flex-row pulse-h-1 pulse-w-full pulse-bg-gray-300',
|
|
12
|
+
'dark:pulse-bg-gray-600',
|
|
13
|
+
system_attributes[:classes]
|
|
14
|
+
)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def before_render
|
|
20
|
+
@system_attributes[:title] ||= t('.title', percentage: @percentage)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|