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,261 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pulse
|
|
4
|
+
# An ActionList is a styled list of links. It acts as the base component for many
|
|
5
|
+
# other menu-type components, including `ActionMenu` and `SelectPanel`, as well as
|
|
6
|
+
# the navigational component `NavList`.
|
|
7
|
+
#
|
|
8
|
+
# Each item in an action list can be augmented by specifying corresponding leading
|
|
9
|
+
# and/or trailing visuals.
|
|
10
|
+
class ActionList < Pulse::Component
|
|
11
|
+
DEFAULT_ROLE = :list
|
|
12
|
+
|
|
13
|
+
MENU_ROLE = :menu
|
|
14
|
+
DEFAULT_MENU_ITEM_ROLE = :menuitem
|
|
15
|
+
|
|
16
|
+
DEFAULT_SCHEME = :full
|
|
17
|
+
SCHEME_MAPPINGS = {
|
|
18
|
+
DEFAULT_SCHEME => nil,
|
|
19
|
+
:inset => 'ActionListWrap--inset'
|
|
20
|
+
}.freeze
|
|
21
|
+
SCHEME_OPTIONS = SCHEME_MAPPINGS.keys.freeze
|
|
22
|
+
|
|
23
|
+
DEFAULT_SELECT_VARIANT = :none
|
|
24
|
+
SELECT_VARIANT_OPTIONS = [
|
|
25
|
+
:single,
|
|
26
|
+
:multiple,
|
|
27
|
+
:multiple_checkbox,
|
|
28
|
+
DEFAULT_SELECT_VARIANT
|
|
29
|
+
].freeze
|
|
30
|
+
|
|
31
|
+
SELECT_VARIANT_ROLE_MAP = {
|
|
32
|
+
single: :menuitemradio,
|
|
33
|
+
multiple: :menuitemcheckbox,
|
|
34
|
+
multiple_checkbox: :menuitemcheckbox
|
|
35
|
+
}.freeze
|
|
36
|
+
|
|
37
|
+
# simplecov:disable
|
|
38
|
+
# @private
|
|
39
|
+
def self.custom_element_name
|
|
40
|
+
@custom_element_name ||= name.split('::').last.underscore.dasherize
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @private
|
|
44
|
+
delegate :custom_element_name, to: :class
|
|
45
|
+
# simplecov:enable
|
|
46
|
+
|
|
47
|
+
# Heading text rendered above the list of items.
|
|
48
|
+
#
|
|
49
|
+
# @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Pulse::ActionList::Heading) %>.
|
|
50
|
+
renders_one :heading, lambda { |**system_arguments|
|
|
51
|
+
Heading.new(**system_arguments)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# @!parse
|
|
55
|
+
# # Adds an item to the list.
|
|
56
|
+
# #
|
|
57
|
+
# # @param component_klass [Class] The class to use instead of the default <%= link_to_component(Pulse::ActionList::Item) %>
|
|
58
|
+
# # @param system_arguments [Hash] These arguments are forwarded to <%= link_to_component(Pulse::ActionList::Item) %>, or whatever class is passed as the `component_klass` argument.
|
|
59
|
+
# def with_item(**system_arguments, &block)
|
|
60
|
+
# end
|
|
61
|
+
|
|
62
|
+
# @!parse
|
|
63
|
+
# # Adds a divider to the list. Dividers visually separate items.
|
|
64
|
+
# #
|
|
65
|
+
# # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Pulse::ActionList::Divider) %>.
|
|
66
|
+
# def with_divider(**system_arguments, &block)
|
|
67
|
+
# end
|
|
68
|
+
|
|
69
|
+
# @!parse
|
|
70
|
+
# # Adds an avatar item to the list. Avatar items are a convenient way to accessibly add an item with a leading avatar image.
|
|
71
|
+
# #
|
|
72
|
+
# # @param src [String] The source url of the avatar image.
|
|
73
|
+
# # @param username [String] The username associated with the avatar.
|
|
74
|
+
# # @param full_name [String] Optional. The user's full name.
|
|
75
|
+
# # @param full_name_scheme [Symbol] Optional. How to display the user's full name. <%= one_of(Pulse::ActionList::Item::DESCRIPTION_SCHEME_OPTIONS) %>
|
|
76
|
+
# # @param component_klass [Class] The class to use instead of the default <%= link_to_component(Pulse::ActionList::Item) %>
|
|
77
|
+
# # @param avatar_arguments [Hash] Optional. The arguments accepted by <%= link_to_component(Pulse::Avatar) %>
|
|
78
|
+
# # @param system_arguments [Hash] These arguments are forwarded to <%= link_to_component(Pulse::ActionList::Item) %>, or whatever class is passed as the `component_klass` argument.
|
|
79
|
+
# def with_avatar_item(src:, username:, full_name: nil, full_name_scheme: Pulse::ActionList::Item::DEFAULT_DESCRIPTION_SCHEME, component_klass: ActionList::Item, avatar_arguments: {}, **system_arguments, &block)
|
|
80
|
+
# end
|
|
81
|
+
|
|
82
|
+
# Items. Items can be individual items, avatar items, or dividers. See the documentation for `#with_item`, `#with_divider`, and `#with_avatar_item` respectively for more information.
|
|
83
|
+
#
|
|
84
|
+
renders_many :items, types: {
|
|
85
|
+
item: {
|
|
86
|
+
renders: lambda { |**system_arguments|
|
|
87
|
+
build_item(**system_arguments).tap do |item|
|
|
88
|
+
will_add_item(item)
|
|
89
|
+
end
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
as: :item
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
avatar_item: {
|
|
96
|
+
renders: lambda { |**system_arguments|
|
|
97
|
+
build_avatar_item(**system_arguments).tap do |item|
|
|
98
|
+
will_add_item(item)
|
|
99
|
+
end
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
as: :avatar_item
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
divider: {
|
|
106
|
+
renders: ActionList::Divider,
|
|
107
|
+
as: :divider
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
attr_reader :id, :select_variant, :role
|
|
112
|
+
|
|
113
|
+
# @param id [String] HTML ID value.
|
|
114
|
+
# @param role [Boolean] ARIA role describing the function of the list. listbox and menu are a common values.
|
|
115
|
+
# @param item_classes [String] Additional CSS classes to attach to items.
|
|
116
|
+
# @param scheme [Symbol] <%= one_of(Pulse::ActionList::SCHEME_OPTIONS) %> `inset` children are offset (vertically and horizontally) from list edges. `full` (default) children are flush (vertically and horizontally) with list edges.
|
|
117
|
+
# @param show_dividers [Boolean] Display a divider above each item in the list when it does not follow a header or divider.
|
|
118
|
+
# @param select_variant [Symbol] How items may be selected in the list. <%= one_of(Pulse::ActionList::SELECT_VARIANT_OPTIONS) %>
|
|
119
|
+
# @param form_arguments [Hash] Allows an `ActionList` to act as a select list in multi- and single-select modes. Pass the `builder:` and `name:` options to this hash. `builder:` should be an instance of `ActionView::Helpers::FormBuilder`, which are created by the standard Rails `#form_with` and `#form_for` helpers. The `name:` option is the desired name of the field that will be included in the params sent to the server on form submission. *NOTE*: Consider using an <%= link_to_component(Pulse::ActionMenu) %> instead of using this feature directly.
|
|
120
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
|
121
|
+
def initialize(
|
|
122
|
+
id: self.class.generate_id,
|
|
123
|
+
role: nil,
|
|
124
|
+
item_classes: nil,
|
|
125
|
+
scheme: DEFAULT_SCHEME,
|
|
126
|
+
show_dividers: false,
|
|
127
|
+
select_variant: DEFAULT_SELECT_VARIANT,
|
|
128
|
+
form_arguments: {},
|
|
129
|
+
**system_arguments
|
|
130
|
+
)
|
|
131
|
+
@system_arguments = system_arguments
|
|
132
|
+
@id = id
|
|
133
|
+
@system_arguments[:id] = @id
|
|
134
|
+
@system_arguments[:tag] = :ul
|
|
135
|
+
@item_classes = item_classes
|
|
136
|
+
@scheme = fetch_or_fallback(SCHEME_OPTIONS, scheme, DEFAULT_SCHEME)
|
|
137
|
+
@show_dividers = show_dividers
|
|
138
|
+
@select_variant = select_variant
|
|
139
|
+
@system_arguments[:classes] = merge_classes(
|
|
140
|
+
SCHEME_MAPPINGS[@scheme],
|
|
141
|
+
system_arguments[:classes],
|
|
142
|
+
'ActionListWrap--divided' => @show_dividers
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
@role = role || (allows_selection? ? MENU_ROLE : DEFAULT_ROLE)
|
|
146
|
+
@system_arguments[:role] = @role
|
|
147
|
+
|
|
148
|
+
@list_wrapper_arguments = {}
|
|
149
|
+
|
|
150
|
+
@form_builder = form_arguments[:builder]
|
|
151
|
+
@input_name = form_arguments[:name]
|
|
152
|
+
|
|
153
|
+
return unless required_form_arguments_given? && !allows_selection?
|
|
154
|
+
|
|
155
|
+
raise ArgumentError,
|
|
156
|
+
'lists/menus that act as form inputs must also allow item ' \
|
|
157
|
+
'selection (please pass the `select_variant:` option)'
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# @private
|
|
161
|
+
def before_render
|
|
162
|
+
return unless heading?
|
|
163
|
+
|
|
164
|
+
@system_arguments[:'aria-labelledby'] = heading.title_id
|
|
165
|
+
return unless heading.subtitle?
|
|
166
|
+
|
|
167
|
+
@system_arguments[:'aria-describedby'] =
|
|
168
|
+
heading.subtitle_id
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Builds a new item but does not add it to the list. Use this method
|
|
172
|
+
# instead of the `#with_item` slot if you need to render an item outside
|
|
173
|
+
# the context of a list, eg. if rendering additional items to append to
|
|
174
|
+
# an existing list, perhaps via a separate HTTP request.
|
|
175
|
+
#
|
|
176
|
+
# @param component_klass [Class] The class to use instead of the default <%= link_to_component(Pulse::ActionList::Item) %>
|
|
177
|
+
# @param system_arguments [Hash] These arguments are forwarded to <%= link_to_component(Pulse::ActionList::Item) %>, or whatever class is passed as the `component_klass` argument.
|
|
178
|
+
def build_item(component_klass: ActionList::Item, **system_arguments)
|
|
179
|
+
if single_select? &&
|
|
180
|
+
system_arguments[:active] &&
|
|
181
|
+
items.any?(&:active?)
|
|
182
|
+
raise ArgumentError,
|
|
183
|
+
'only a single item may be active when select_variant is ' \
|
|
184
|
+
'set to :single'
|
|
185
|
+
end
|
|
186
|
+
system_arguments[:classes] = merge_classes(
|
|
187
|
+
@item_classes,
|
|
188
|
+
system_arguments[:classes]
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
component_klass.new(list: self, **system_arguments)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Builds a new avatar item but does not add it to the list. Avatar items
|
|
195
|
+
# are a convenient way to accessibly add an item with a leading avatar
|
|
196
|
+
# image. Use this method instead of the `#with_avatar_item` slot if you
|
|
197
|
+
# need to render an avatar item outside the context of a list, eg. if
|
|
198
|
+
# rendering additional items to append to an existing list, perhaps via
|
|
199
|
+
# a separate HTTP request.
|
|
200
|
+
#
|
|
201
|
+
# @param src [String] The source url of the avatar image.
|
|
202
|
+
# @param username [String] The username associated with the avatar.
|
|
203
|
+
# @param full_name [String] Optional. The user's full name.
|
|
204
|
+
# @param full_name_scheme [Symbol] Optional. How to display the user's full name. <%= one_of(Pulse::ActionList::Item::DESCRIPTION_SCHEME_OPTIONS) %>
|
|
205
|
+
# @param component_klass [Class] The class to use instead of the default <%= link_to_component(Pulse::ActionList::Item) %>
|
|
206
|
+
# @param avatar_arguments [Hash] Optional. The arguments accepted by <%= link_to_component(Pulse::Avatar) %>
|
|
207
|
+
# @param system_arguments [Hash] These arguments are forwarded to <%= link_to_component(Pulse::ActionList::Item) %>, or whatever class is passed as the `component_klass` argument.
|
|
208
|
+
def build_avatar_item(
|
|
209
|
+
src:, username:, full_name: nil,
|
|
210
|
+
full_name_scheme: Pulse::ActionList::Item::DEFAULT_DESCRIPTION_SCHEME,
|
|
211
|
+
component_klass: ActionList::Item, avatar_arguments: {},
|
|
212
|
+
**system_arguments
|
|
213
|
+
)
|
|
214
|
+
build_item(label: username, description_scheme: full_name_scheme,
|
|
215
|
+
component_klass:, **system_arguments).tap do |item|
|
|
216
|
+
item.with_leading_visual_raw_content do
|
|
217
|
+
# no alt text necessary for presentational item
|
|
218
|
+
item.render(Pulse::Avatar.new(src:, **avatar_arguments,
|
|
219
|
+
role: :presentation, size: 16))
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
item.with_description_content(full_name) if full_name
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def single_select?
|
|
227
|
+
select_variant == :single
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def multi_select?
|
|
231
|
+
[:multiple, :multiple_checkbox].include?(select_variant)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def allows_selection?
|
|
235
|
+
single_select? || multi_select?
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def acts_as_menu?
|
|
239
|
+
@system_arguments[:role] == :menu
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def required_form_arguments_given?
|
|
243
|
+
@form_builder && @input_name
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def acts_as_form_input?
|
|
247
|
+
required_form_arguments_given? && allows_selection?
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# @private
|
|
251
|
+
def will_add_item(_item); end
|
|
252
|
+
|
|
253
|
+
private
|
|
254
|
+
|
|
255
|
+
def with_post_list_content(&block)
|
|
256
|
+
@post_list_content_block = block
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
attr_reader :post_list_content_block
|
|
260
|
+
end
|
|
261
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pulse
|
|
4
|
+
# Renders a badge component
|
|
5
|
+
# see: /lookbook/inspect/pulse/badge/default
|
|
6
|
+
class Badge < Pulse::Component
|
|
7
|
+
DEFAULT_TAG = :span
|
|
8
|
+
TAG_OPTIONS = [DEFAULT_TAG, :summary, :a, :div].freeze
|
|
9
|
+
|
|
10
|
+
DEFAULT_VARIANT = :standard
|
|
11
|
+
VARIANT_MAPPINGS = {
|
|
12
|
+
standard: 'pulse-inline-block pulse-rounded pulse-uppercase ' \
|
|
13
|
+
'pulse-py-0.5 pulse-px-1.5 pulse-text-[10px] ' \
|
|
14
|
+
'pulse-font-bold',
|
|
15
|
+
pill: 'pulse-inline-block pulse-rounded-2xl pulse-min-w-[8rem] ' \
|
|
16
|
+
'pulse-text-center pulse-p-2 pulse-text-sm ' \
|
|
17
|
+
'pulse-leading-none pulse-font-bold'
|
|
18
|
+
}.freeze
|
|
19
|
+
VARIANT_OPTIONS = VARIANT_MAPPINGS.keys.freeze
|
|
20
|
+
|
|
21
|
+
DEFAULT_SCHEME = :secondary
|
|
22
|
+
SCHEME_MAPPINGS = {
|
|
23
|
+
primary: 'pulse-bg-primary pulse-text-white hover:pulse-text-white',
|
|
24
|
+
secondary: 'pulse-bg-secondary pulse-text-white',
|
|
25
|
+
secondary_outline: 'pulse-bg-white pulse-border pulse-border-secondary ' \
|
|
26
|
+
'pulse-text-secondary ' \
|
|
27
|
+
'pulse-hover:pulse-text-secondary',
|
|
28
|
+
invited: 'pulse-bg-secondary-200 pulse-text-gray ' \
|
|
29
|
+
'pulse-hover:pulse-text-gray',
|
|
30
|
+
active: 'pulse-bg-success-200 pulse-text-gray hover:pulse-text-gray',
|
|
31
|
+
disabled: 'pulse-bg-gray-100 pulse-text-gray hover:pulse-text-gray',
|
|
32
|
+
info: 'pulse-bg-gray-600 pulse-text-white hover:pulse-text-white'
|
|
33
|
+
}.freeze
|
|
34
|
+
SCHEME_OPTIONS = SCHEME_MAPPINGS.keys.freeze
|
|
35
|
+
|
|
36
|
+
def initialize(tag: DEFAULT_TAG, variant: DEFAULT_VARIANT,
|
|
37
|
+
scheme: DEFAULT_SCHEME, **system_arguments)
|
|
38
|
+
@system_arguments = system_arguments
|
|
39
|
+
|
|
40
|
+
@variant = fetch_or_fallback(VARIANT_OPTIONS, variant, DEFAULT_VARIANT)
|
|
41
|
+
@scheme = fetch_or_fallback(SCHEME_OPTIONS, scheme, DEFAULT_SCHEME)
|
|
42
|
+
|
|
43
|
+
@system_arguments[:tag] = fetch_or_fallback(TAG_OPTIONS, tag, DEFAULT_TAG)
|
|
44
|
+
@system_arguments[:classes] = merge_classes(
|
|
45
|
+
VARIANT_MAPPINGS[@variant],
|
|
46
|
+
SCHEME_MAPPINGS[@scheme],
|
|
47
|
+
system_arguments[:classes]
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def call
|
|
52
|
+
render(Pulse::BaseComponent.new(**@system_arguments)) { content }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def render?
|
|
56
|
+
content.present?
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pulse
|
|
4
|
+
# BaseComponent accepts a standard set of options, providing the
|
|
5
|
+
# foundation for other components to use and build upon. Specify a tag,
|
|
6
|
+
# optional classes, and any other additional options will be passed to
|
|
7
|
+
# Rails' `content_tag` helper.
|
|
8
|
+
#
|
|
9
|
+
# This is a simplified version of Primer's `BaseComponent`
|
|
10
|
+
#
|
|
11
|
+
# For example:
|
|
12
|
+
#
|
|
13
|
+
# ```erb
|
|
14
|
+
# <%= render Pulse::BaseComponent.new(tag: :span, classes: "text-sm") do %>
|
|
15
|
+
# Hello world
|
|
16
|
+
# <% end %>
|
|
17
|
+
# ```
|
|
18
|
+
#
|
|
19
|
+
# Renders:
|
|
20
|
+
#
|
|
21
|
+
# ```html
|
|
22
|
+
# <span class="text-sm">Hello world</span>
|
|
23
|
+
# ```
|
|
24
|
+
#
|
|
25
|
+
class BaseComponent < Pulse::Component
|
|
26
|
+
def initialize(tag:, classes: nil, **system_arguments)
|
|
27
|
+
@tag = tag
|
|
28
|
+
@classes = classes
|
|
29
|
+
@system_arguments = system_arguments
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def call
|
|
33
|
+
content_tag(@tag, content, class: @classes, **@system_arguments)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/! components/ui/blank_slate
|
|
2
|
+
= render Pulse::BaseComponent.new(**@system_arguments) do
|
|
3
|
+
/ div class="flex items-center justify-center mb-4"
|
|
4
|
+
/ = render_svg 'blank-graphic'
|
|
5
|
+
= render Pulse::Text.new(type: :body1) do
|
|
6
|
+
= title || t('.empty')
|
|
7
|
+
|
|
8
|
+
- if description.present?
|
|
9
|
+
= render Pulse::Text.new(type: :body4) do
|
|
10
|
+
= description
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pulse
|
|
4
|
+
# Use as placeholder when content is missing
|
|
5
|
+
class BlankSlate < Pulse::Component
|
|
6
|
+
renders_one :title
|
|
7
|
+
renders_one :description
|
|
8
|
+
|
|
9
|
+
def initialize(**system_arguments)
|
|
10
|
+
@system_arguments = system_arguments
|
|
11
|
+
@system_arguments[:tag] ||= :div
|
|
12
|
+
@system_arguments[:classes] = merge_classes(
|
|
13
|
+
'pulse-container pulse-text-center pulse-pt-16 pulse-pb-20',
|
|
14
|
+
'pulse-bg-secondary-50',
|
|
15
|
+
system_arguments[:classes]
|
|
16
|
+
)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pulse
|
|
4
|
+
class BorderBox
|
|
5
|
+
# `BorderBox::Header` is used inside `BorderBox` to render its header slot.
|
|
6
|
+
#
|
|
7
|
+
# @accessibility When using `header.with_title`, set `tag` to one of `h1`, `h2`, `h3`, etc. based on what is appropriate for the page context. <%= link_to_heading_practices %>
|
|
8
|
+
class Header < Pulse::Component
|
|
9
|
+
attr_reader :id
|
|
10
|
+
|
|
11
|
+
TITLE_TAG_FALLBACK = :h2
|
|
12
|
+
TITLE_TAG_OPTIONS = [:h1, TITLE_TAG_FALLBACK, :h3, :h4, :h5, :h6].freeze
|
|
13
|
+
|
|
14
|
+
# Optional Title.
|
|
15
|
+
#
|
|
16
|
+
# @param tag [Symbol] <%= one_of(Pulse::BorderBox::Header::TITLE_TAG_OPTIONS) %>
|
|
17
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
|
18
|
+
renders_one :title, lambda { |tag:, **system_arguments|
|
|
19
|
+
system_arguments[:tag] = fetch_or_fallback(
|
|
20
|
+
TITLE_TAG_OPTIONS,
|
|
21
|
+
tag,
|
|
22
|
+
TITLE_TAG_FALLBACK
|
|
23
|
+
)
|
|
24
|
+
system_arguments[:classes] = class_names(
|
|
25
|
+
'Box-title',
|
|
26
|
+
system_arguments[:classes]
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
Pulse::BaseComponent.new(**system_arguments)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
|
33
|
+
def initialize(**system_arguments)
|
|
34
|
+
@system_arguments = system_arguments
|
|
35
|
+
@system_arguments[:id] ||= self.class.generate_id
|
|
36
|
+
@system_arguments[:tag] = :div
|
|
37
|
+
@system_arguments[:classes] = class_names(
|
|
38
|
+
'pulse-px-4 pulse-py-5 pulse-bg-gray-50 sm:pulse-p-6',
|
|
39
|
+
'pulse-rounded-t-md pulse-border-b pulse-border-gray-300',
|
|
40
|
+
'pulse-border-solid',
|
|
41
|
+
'Box-header',
|
|
42
|
+
system_arguments[:classes]
|
|
43
|
+
)
|
|
44
|
+
@id = @system_arguments[:id]
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pulse
|
|
4
|
+
# `BorderBox` is a Box component with a border.
|
|
5
|
+
class BorderBox < Pulse::Component
|
|
6
|
+
DEFAULT_PADDING = :default
|
|
7
|
+
PADDING_MAPPINGS = {
|
|
8
|
+
DEFAULT_PADDING => '',
|
|
9
|
+
:condensed => 'Box--condensed',
|
|
10
|
+
:spacious => 'Box--spacious'
|
|
11
|
+
}.freeze
|
|
12
|
+
PADDING_SUGGESTION = "Perhaps you could consider using :padding options " \
|
|
13
|
+
"of #{PADDING_MAPPINGS.keys.to_sentence}?".freeze
|
|
14
|
+
|
|
15
|
+
DEFAULT_ROW_SCHEME = :default
|
|
16
|
+
ROW_SCHEME_MAPPINGS = {
|
|
17
|
+
DEFAULT_ROW_SCHEME => '',
|
|
18
|
+
:neutral => 'Box-row--gray',
|
|
19
|
+
:info => 'Box-row--blue',
|
|
20
|
+
:warning => 'Box-row--yellow'
|
|
21
|
+
}.freeze
|
|
22
|
+
|
|
23
|
+
# Optional Header.
|
|
24
|
+
#
|
|
25
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
|
26
|
+
# @accessibility
|
|
27
|
+
# When using header.with_title, the recommended tag is a heading tag, such as h1, h2, h3, etc.
|
|
28
|
+
renders_one :header, 'Pulse::BorderBox::Header'
|
|
29
|
+
|
|
30
|
+
# Optional Body.
|
|
31
|
+
#
|
|
32
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
|
33
|
+
renders_one :body, lambda { |**system_arguments|
|
|
34
|
+
system_arguments[:tag] = :div
|
|
35
|
+
system_arguments[:classes] = class_names(
|
|
36
|
+
'pulse-px-4 pulse-py-5 pulse-bg-white sm:pulse-p-6',
|
|
37
|
+
'pulse-border-b pulse-border-gray-300 pulse-border-solid',
|
|
38
|
+
'first-of-type:pulse-rounded-t-md',
|
|
39
|
+
'last-of-type:pulse-rounded-b-md',
|
|
40
|
+
'last-of-type:pulse-border-b-0',
|
|
41
|
+
system_arguments[:classes]
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
Pulse::BaseComponent.new(**system_arguments)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
# Optional Footer.
|
|
48
|
+
#
|
|
49
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
|
50
|
+
renders_one :footer, lambda { |**system_arguments|
|
|
51
|
+
system_arguments[:tag] = :div
|
|
52
|
+
system_arguments[:classes] = class_names(
|
|
53
|
+
'pulse-px-4 pulse-py-5 pulse-bg-white sm:pulse-p-6',
|
|
54
|
+
'pulse-border-t pulse-border-gray-300 pulse-border-solid',
|
|
55
|
+
'-pulse-mt-1 pulse-rounded-b-md',
|
|
56
|
+
'Box-footer',
|
|
57
|
+
system_arguments[:classes]
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
Pulse::BaseComponent.new(**system_arguments)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# @apply pulse-pt-[var(--stack-padding-normal)] pulse-pr-[var(--stack-padding-normal)] pulse-pb-[var(--stack-padding-normal)] pulse-pl-[var(--stack-padding-normal)] pulse-mt-[calc(var(--borderWidth-thin)_*_-1)] pulse-list-none pulse-border-t-[color:var(--borderColor-muted)] pulse-border-t-[length:var(--borderWidth-thin)] first-of-type:pulse-rounded-tl-[var(--borderRadius-medium)] first-of-type:pulse-rounded-tr-[var(--borderRadius-medium)] last-of-type:pulse-rounded-br-[var(--borderRadius-medium)] last-of-type:pulse-rounded-bl-[var(--borderRadius-medium)]; /* To account for applying Box component to a list */
|
|
64
|
+
# border-top-style: solid;
|
|
65
|
+
|
|
66
|
+
# Use Rows to add rows with borders and maintain the same padding.
|
|
67
|
+
#
|
|
68
|
+
# @param scheme [Symbol] Color scheme. <%= one_of(Pulse::BorderBox::ROW_SCHEME_MAPPINGS.keys) %>
|
|
69
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
|
70
|
+
renders_many :rows, lambda { |scheme: DEFAULT_ROW_SCHEME,
|
|
71
|
+
**system_arguments|
|
|
72
|
+
system_arguments[:tag] = :li
|
|
73
|
+
system_arguments[:classes] = class_names(
|
|
74
|
+
'pulse-px-4 pulse-py-5 pulse-bg-white sm:pulse-p-6',
|
|
75
|
+
'pulse-border-b pulse-border-gray-300 pulse-border-solid',
|
|
76
|
+
'last:pulse-border-b-0',
|
|
77
|
+
'first-of-type:pulse-rounded-t-md last-of-type:pulse-rounded-b-md',
|
|
78
|
+
ROW_SCHEME_MAPPINGS[fetch_or_fallback(ROW_SCHEME_MAPPINGS.keys,
|
|
79
|
+
scheme, DEFAULT_ROW_SCHEME)],
|
|
80
|
+
system_arguments[:classes]
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
Pulse::BaseComponent.new(**system_arguments)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
# @param padding [Symbol] <%= one_of(Pulse::BorderBox::PADDING_MAPPINGS.keys) %>
|
|
87
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
|
88
|
+
def initialize(padding: DEFAULT_PADDING, **system_arguments)
|
|
89
|
+
@system_arguments = deny_tag_argument(**system_arguments)
|
|
90
|
+
@system_arguments[:tag] = :div
|
|
91
|
+
@system_arguments[:classes] = class_names(
|
|
92
|
+
'pulse-bg-white pulse-border pulse-border-gray-300 pulse-border-solid',
|
|
93
|
+
'pulse-border-[1px] pulse-rounded-md',
|
|
94
|
+
PADDING_MAPPINGS[fetch_or_fallback(PADDING_MAPPINGS.keys, padding,
|
|
95
|
+
DEFAULT_PADDING)],
|
|
96
|
+
system_arguments[:classes]
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
@system_arguments[:system_arguments_denylist] =
|
|
100
|
+
{ [:p, :pt, :pb, :pr, :pl] => PADDING_SUGGESTION }
|
|
101
|
+
@list_arguments = {
|
|
102
|
+
tag: :ul,
|
|
103
|
+
class: class_names
|
|
104
|
+
# 'pulse-divide-y pulse-divide-gray-300',
|
|
105
|
+
# 'pulse-border-b pulse-border-gray-300 pulse-border-solid'
|
|
106
|
+
|
|
107
|
+
}
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def render?
|
|
111
|
+
rows.any? || header.present? || body.present? || footer.present?
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
private
|
|
115
|
+
|
|
116
|
+
def before_render
|
|
117
|
+
return unless header
|
|
118
|
+
|
|
119
|
+
@list_arguments[:aria] = {
|
|
120
|
+
labelledby: header.id
|
|
121
|
+
}
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pulse
|
|
4
|
+
# `Box` is a basic wrapper component for most layout related needs.
|
|
5
|
+
class Box < Pulse::Component
|
|
6
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
|
7
|
+
def initialize(**system_arguments)
|
|
8
|
+
@system_arguments = deny_tag_argument(**system_arguments)
|
|
9
|
+
|
|
10
|
+
@system_arguments[:tag] = :div
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call
|
|
14
|
+
render(Pulse::BaseComponent.new(**@system_arguments)) { content }
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pulse
|
|
4
|
+
# Renders a breadcrumb item
|
|
5
|
+
class BreadcrumbItem < Pulse::Component
|
|
6
|
+
DEFAULT_LINK_CLASSES = 'pulse-underline'
|
|
7
|
+
DEFAULT_TEXT_CLASSES = ''
|
|
8
|
+
|
|
9
|
+
attr_reader :text, :href
|
|
10
|
+
|
|
11
|
+
def initialize(text:, href: nil, **system_arguments)
|
|
12
|
+
@text = text
|
|
13
|
+
@href = href
|
|
14
|
+
@classes = system_arguments[:classes]
|
|
15
|
+
@system_arguments = system_arguments.except(:classes)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def link?
|
|
19
|
+
@href.present?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def call
|
|
23
|
+
if link?
|
|
24
|
+
link_to(@text, @href, class: link_classes, **@system_arguments)
|
|
25
|
+
else
|
|
26
|
+
content_tag(:span, @text, class: text_classes, **@system_arguments)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def link_classes
|
|
33
|
+
merge_classes(DEFAULT_LINK_CLASSES, @classes)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def text_classes
|
|
37
|
+
merge_classes(DEFAULT_TEXT_CLASSES, @classes)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/! components/pulse/breadcrumbs
|
|
2
|
+
= render Pulse::BaseComponent.new(**@system_arguments) do
|
|
3
|
+
ol class="pulse-list-none pulse-list-reset pulse-flex pulse-m-0 pulse-text-xs"
|
|
4
|
+
- items.filter { |i| i.text.present? }.each_with_index do |item, idx|
|
|
5
|
+
- if idx != 0
|
|
6
|
+
li class="pulse-mx-1" aria-hidden="true"
|
|
7
|
+
| >
|
|
8
|
+
li
|
|
9
|
+
= item
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pulse
|
|
4
|
+
# Renders a breadcrumb navigation list
|
|
5
|
+
class Breadcrumbs < Pulse::Component
|
|
6
|
+
renders_many :items, Pulse::BreadcrumbItem
|
|
7
|
+
|
|
8
|
+
def initialize(**system_arguments)
|
|
9
|
+
@system_arguments = system_arguments
|
|
10
|
+
@system_arguments[:tag] = :nav
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def render?
|
|
14
|
+
items.any?
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|