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,10 @@
|
|
|
1
|
+
/! components/pulse/tables/cell
|
|
2
|
+
= render Pulse::BaseComponent.new(**options) do
|
|
3
|
+
- if sortable?
|
|
4
|
+
= link_to request.params.merge(**url_options), class: merge_classes('pulse-inline-flex pulse-items-center pulse-gap-1', title_classes) do
|
|
5
|
+
= content
|
|
6
|
+
= render Pulse::Icon.new(icon: icon, width: 9, height: 8, classes: 'pulse-shrink-0', title: t(".#{icon}"))
|
|
7
|
+
|
|
8
|
+
- else
|
|
9
|
+
span class=title_classes
|
|
10
|
+
= content
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pulse
|
|
4
|
+
class Table
|
|
5
|
+
# Renders a table cell
|
|
6
|
+
class Cell < Pulse::Component
|
|
7
|
+
attr_reader :sort_by, :current_sort_column, :current_sort_direction,
|
|
8
|
+
:additional_params, :footer
|
|
9
|
+
|
|
10
|
+
HEAD_DEFAULT_SCHEME = :xs
|
|
11
|
+
HEAD_SCHEME_MAPPINGS = {
|
|
12
|
+
sm: 'pulse-px-3 pulse-py-4 print:pulse-px-2 print:pulse-py-3 ' \
|
|
13
|
+
'pulse-text-sm print:pulse-text-xs pulse-font-base',
|
|
14
|
+
xs: 'pulse-px-2 pulse-py-4 print:pulse-py-2 pulse-text-xs ' \
|
|
15
|
+
'print:pulse-text-2xs pulse-font-medium'
|
|
16
|
+
}.freeze
|
|
17
|
+
HEAD_SCHEME_OPTIONS = HEAD_SCHEME_MAPPINGS.keys.freeze
|
|
18
|
+
|
|
19
|
+
DEFAULT_CELL_CLASSES = 'pulse-text-start first:pulse-ps-4 ' \
|
|
20
|
+
'last:pulse-pe-4 pulse-px-2 pulse-text-gray ' \
|
|
21
|
+
'print:first:pulse-ps-2 print:last:pulse-pe-2'
|
|
22
|
+
DEFAULT_TH_CLASSES = 'pulse-uppercase pulse-tracking-wider'
|
|
23
|
+
DEFAULT_TD_CLASSES = 'pulse-py-2 pulse-whitespace-nowrap ' \
|
|
24
|
+
'pulse-text-sm print:pulse-text-xs pulse-text-gray'
|
|
25
|
+
|
|
26
|
+
def initialize(sort_by: nil, current_sort_column: nil,
|
|
27
|
+
current_sort_direction: nil, additional_params: {},
|
|
28
|
+
head_scheme: HEAD_DEFAULT_SCHEME, footer: nil,
|
|
29
|
+
title_classes: '', **options)
|
|
30
|
+
@sort_by = sort_by
|
|
31
|
+
@current_sort_column = current_sort_column
|
|
32
|
+
@current_sort_direction = current_sort_direction
|
|
33
|
+
@additional_params = additional_params
|
|
34
|
+
@footer = footer
|
|
35
|
+
@title_classes = title_classes
|
|
36
|
+
@options = options
|
|
37
|
+
@options[:classes] = merge_classes(
|
|
38
|
+
DEFAULT_CELL_CLASSES,
|
|
39
|
+
header_or_footer? ? DEFAULT_TH_CLASSES : DEFAULT_TD_CLASSES,
|
|
40
|
+
header_or_footer? ? HEAD_SCHEME_MAPPINGS[head_scheme] : '',
|
|
41
|
+
options[:classes]
|
|
42
|
+
)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def options
|
|
46
|
+
{ tag: 'td' }.deep_merge(@options).tap do |args|
|
|
47
|
+
args[:classes] = class_names(
|
|
48
|
+
args[:classes]
|
|
49
|
+
)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def title_classes
|
|
54
|
+
header_or_footer? ? @title_classes : ''
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def active
|
|
58
|
+
sort_by && sort_by == current_sort_column
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def sortable?
|
|
62
|
+
sort_by && params[:q].blank?
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def dir
|
|
66
|
+
current_sort_direction
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def icon
|
|
70
|
+
active ? "sort_#{dir}" : 'sort'
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def header_or_footer?
|
|
74
|
+
@options[:tag] == 'th' || @footer
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def url_options
|
|
78
|
+
{ sort: sort_by, direction: }.merge(additional_params)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def direction
|
|
82
|
+
params[:sort].nil? || params[:sort] == sort_by ? sort_dir : nil
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def sort_dir
|
|
86
|
+
params[:direction] == 'asc' ? 'desc' : 'asc'
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pulse
|
|
4
|
+
class Table
|
|
5
|
+
# Encapsulates table column properties
|
|
6
|
+
class Column < Pulse::Component
|
|
7
|
+
attr_reader :title, :sr_title, :sort_by, :footer, :options
|
|
8
|
+
|
|
9
|
+
def initialize(title: '', sr_title: nil, sort_by: nil, footer: nil,
|
|
10
|
+
**options, &block)
|
|
11
|
+
@title = title
|
|
12
|
+
@sr_title = sr_title
|
|
13
|
+
@sort_by = sort_by
|
|
14
|
+
@footer = footer
|
|
15
|
+
@block = block
|
|
16
|
+
@options = options
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
delegate :call, to: :@block
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/! components/pulse/table
|
|
2
|
+
- if collection.present?
|
|
3
|
+
div class="pulse-flex pulse-flex-col pulse-gap-4"
|
|
4
|
+
div class=class_names("pulse-w-full pulse-shadow print:pulse-shadow-none sm:pulse-rounded-lg", 'pulse-overflow-x-scroll': scroll)
|
|
5
|
+
= render Pulse::BaseComponent.new(**table_options) do
|
|
6
|
+
- if header
|
|
7
|
+
= render Pulse::BaseComponent.new(tag: :thead, classes: head_classes) do
|
|
8
|
+
tr
|
|
9
|
+
- columns.each_with_index do |column, index|
|
|
10
|
+
= render_cell( \
|
|
11
|
+
sr_title: column.sr_title,
|
|
12
|
+
sort_by: (column.sort_by unless collection.empty?),
|
|
13
|
+
current_sort_column: current_sort_column,
|
|
14
|
+
current_sort_direction: current_sort_direction,
|
|
15
|
+
tag: 'th',
|
|
16
|
+
**column.options \
|
|
17
|
+
) do
|
|
18
|
+
= column.title&.html_safe
|
|
19
|
+
|
|
20
|
+
= render Pulse::BaseComponent.new(tag: :tbody, classes: body_classes) do
|
|
21
|
+
- collection.each do |row|
|
|
22
|
+
= render Pulse::BaseComponent.new(**row_arguments(row)) do
|
|
23
|
+
- columns.each_with_index do |column, index|
|
|
24
|
+
= render_cell( \
|
|
25
|
+
tag: 'td',
|
|
26
|
+
**column.options \
|
|
27
|
+
) do
|
|
28
|
+
= column.call(row)
|
|
29
|
+
|
|
30
|
+
- if footer?
|
|
31
|
+
tr class=head_classes
|
|
32
|
+
- columns.each_with_index do |column, index|
|
|
33
|
+
= render_cell( \
|
|
34
|
+
tag: 'td',
|
|
35
|
+
footer: true,
|
|
36
|
+
**column.options \
|
|
37
|
+
) do
|
|
38
|
+
= column.footer
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
- if pagination? && collection.respond_to?(:total_pages)
|
|
42
|
+
div class="pulse-text-center pulse-text-gray"
|
|
43
|
+
= paginate(collection, window: 9)
|
|
44
|
+
|
|
45
|
+
- else
|
|
46
|
+
- if empty_state?
|
|
47
|
+
= empty_state
|
|
48
|
+
- else
|
|
49
|
+
= render Pulse::BlankSlate.new do |c|
|
|
50
|
+
- c.with_title { t('.no_data') }
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pulse
|
|
4
|
+
# Renders a table
|
|
5
|
+
# Inspiration taken from
|
|
6
|
+
# https://github.com/baoagency/polaris_view_components/
|
|
7
|
+
class Table < Pulse::Component
|
|
8
|
+
DEFAULT_TABLE_CLASSES = 'pulse-min-w-full pulse-divide-y ' \
|
|
9
|
+
'pulse-divide-gray-300'
|
|
10
|
+
DEFAULT_HEAD_CLASSES = 'pulse-bg-gray-100'
|
|
11
|
+
DEFAULT_TH_CLASSES = 'pulse-text-start pulse-text-gray pulse-uppercase ' \
|
|
12
|
+
'pulse-tracking-wider pulse-px-4 print:pulse-px-2 ' \
|
|
13
|
+
'pulse-py-4 print:pulse-py-3 text-xs pulse-font-medium'
|
|
14
|
+
DEFAULT_BODY_CLASSES = 'pulse-bg-white pulse-divide-y pulse-text-sm ' \
|
|
15
|
+
'pulse-leading-extra-snug pulse-divide-gray-300 ' \
|
|
16
|
+
'print:pulse-divide-gray-400 pulse-tracking-tight ' \
|
|
17
|
+
''
|
|
18
|
+
DEFAULT_ROW_CLASSES = 'pulse-border-secondary-300'
|
|
19
|
+
DEFAULT_TD_CLASSES = 'pulse-px-4 print:pulse-px-2 pulse-py-4'
|
|
20
|
+
|
|
21
|
+
delegate :paginate, to: :helpers
|
|
22
|
+
|
|
23
|
+
renders_many :columns, lambda { |**options, &block|
|
|
24
|
+
Pulse::Table::Column.new(**options, &block)
|
|
25
|
+
}
|
|
26
|
+
renders_one :empty_state, Pulse::BlankSlate
|
|
27
|
+
|
|
28
|
+
attr_reader :collection, :additional_params, :pagination, :striped,
|
|
29
|
+
:full_width, :current_sort_column, :current_sort_direction,
|
|
30
|
+
:hoverable, :row_classes, :header, :scroll
|
|
31
|
+
|
|
32
|
+
def initialize(collection, additional_params: {}, pagination: true,
|
|
33
|
+
striped: false, current_sort_column: nil,
|
|
34
|
+
current_sort_direction: nil, hoverable: false,
|
|
35
|
+
row_classes: nil, header: true, scroll: true, **options)
|
|
36
|
+
@collection = collection
|
|
37
|
+
@additional_params = additional_params
|
|
38
|
+
@pagination = pagination
|
|
39
|
+
@striped = striped
|
|
40
|
+
@current_sort_column = current_sort_column
|
|
41
|
+
@current_sort_direction = current_sort_direction
|
|
42
|
+
@hoverable = hoverable
|
|
43
|
+
@row_classes = row_classes
|
|
44
|
+
@header = header
|
|
45
|
+
@scroll = scroll
|
|
46
|
+
@options = options
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def pagination?
|
|
50
|
+
pagination
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def footer?
|
|
54
|
+
columns.any?(&:footer)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def table_options
|
|
58
|
+
{ tag: 'table' }.deep_merge(@options).tap do |args|
|
|
59
|
+
args[:classes] = merge_classes(
|
|
60
|
+
DEFAULT_TABLE_CLASSES,
|
|
61
|
+
'pulse-container pulse-mx-auto',
|
|
62
|
+
args[:classes]
|
|
63
|
+
)
|
|
64
|
+
args[:data] ||= {}
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def head_classes
|
|
69
|
+
merge_classes(
|
|
70
|
+
DEFAULT_HEAD_CLASSES,
|
|
71
|
+
@options[:head_classes]
|
|
72
|
+
)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def head_cell_classes
|
|
76
|
+
merge_classes(
|
|
77
|
+
DEFAULT_TH_CLASSES
|
|
78
|
+
)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def body_classes
|
|
82
|
+
merge_classes(
|
|
83
|
+
DEFAULT_BODY_CLASSES,
|
|
84
|
+
'[&>tr:nth-child(even)]:pulse-bg-gray-50': striped,
|
|
85
|
+
'pulse-border-b [&>tr]:pulse-border-t [&>tr:first-child]:pulse-border-t-0': !striped # rubocop:disable Layout/LineLength
|
|
86
|
+
)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def row_arguments(row)
|
|
90
|
+
{ tag: 'tr' }.tap do |args|
|
|
91
|
+
args[:classes] = merge_classes(
|
|
92
|
+
DEFAULT_ROW_CLASSES,
|
|
93
|
+
{ 'hover:pulse-bg-dark-charcoal-50': hoverable },
|
|
94
|
+
row_classes
|
|
95
|
+
)
|
|
96
|
+
args[:id] = dom_id(row) if row.respond_to?(:to_key)
|
|
97
|
+
args[:data] ||= {}
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def render_cell(**arguments, &)
|
|
102
|
+
render(
|
|
103
|
+
Pulse::Table::Cell.new(
|
|
104
|
+
additional_params:,
|
|
105
|
+
**arguments
|
|
106
|
+
),
|
|
107
|
+
&
|
|
108
|
+
)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pulse
|
|
4
|
+
# Base Text component
|
|
5
|
+
class Text < Pulse::Component
|
|
6
|
+
STYLE_MAPPINGS = {
|
|
7
|
+
h1: 'pulse-text-3xl pulse-font-bold print:pulse-text-2xl',
|
|
8
|
+
h2: 'pulse-text-2xl pulse-font-bold print:pulse-text-xl',
|
|
9
|
+
h3: 'pulse-text-xl pulse-font-bold print:pulse-text-lg',
|
|
10
|
+
h4: 'pulse-text-lg pulse-font-bold print:pulse-text-base',
|
|
11
|
+
subtitle: 'pulse-text-xl print:pulse-text-lg pulse-leading-tight ' \
|
|
12
|
+
'pulse-font-bold tracking-tight',
|
|
13
|
+
body1: 'pulse-text-lg print:pulse-text-base pulse-font-bold ' \
|
|
14
|
+
'pulse-leading-6 tracking-tight',
|
|
15
|
+
body2: 'pulse-text-lg print:pulse-text-base pulse-font-normal ' \
|
|
16
|
+
'pulse-leading-6 tracking-tight',
|
|
17
|
+
body3: 'pulse-text-base print:pulse-text-sm pulse-font-bold ' \
|
|
18
|
+
'pulse-leading-normal',
|
|
19
|
+
body4: 'pulse-text-base print:pulse-text-sm pulse-font-normal ' \
|
|
20
|
+
'pulse-leading-normal',
|
|
21
|
+
body5: 'pulse-text-sm print:pulse-text-xs pulse-font-bold ' \
|
|
22
|
+
'pulse-leading-snug',
|
|
23
|
+
body6: 'pulse-text-sm print:pulse-text-xs pulse-font-normal ' \
|
|
24
|
+
'pulse-leading-snug',
|
|
25
|
+
body7: 'pulse-text-xs pulse-font-bold pulse-leading-5',
|
|
26
|
+
body8: 'pulse-text-xs pulse-font-normal pulse-leading-5',
|
|
27
|
+
caption1: 'pulse-text-2xs pulse-font-bold tracking-normal ' \
|
|
28
|
+
'pulse-leading-extra-tight',
|
|
29
|
+
caption2: 'pulse-text-2xs pulse-font-normal tracking-normal ' \
|
|
30
|
+
'pulse-leading-extra-tight'
|
|
31
|
+
}.freeze
|
|
32
|
+
|
|
33
|
+
TAG_MAPPINGS = {
|
|
34
|
+
h1: :h1,
|
|
35
|
+
h2: :h2,
|
|
36
|
+
h3: :h3,
|
|
37
|
+
h4: :h4,
|
|
38
|
+
subtitle: :h2,
|
|
39
|
+
body1: :p,
|
|
40
|
+
body2: :p,
|
|
41
|
+
body3: :p,
|
|
42
|
+
body4: :p,
|
|
43
|
+
body5: :p,
|
|
44
|
+
body6: :p,
|
|
45
|
+
body7: :p,
|
|
46
|
+
body8: :p,
|
|
47
|
+
caption1: :p,
|
|
48
|
+
caption2: :p
|
|
49
|
+
}.freeze
|
|
50
|
+
|
|
51
|
+
def initialize(type:, **system_arguments)
|
|
52
|
+
@system_arguments = system_arguments
|
|
53
|
+
@system_arguments[:classes] = merge_classes(
|
|
54
|
+
STYLE_MAPPINGS[type],
|
|
55
|
+
system_arguments[:classes]
|
|
56
|
+
)
|
|
57
|
+
@system_arguments[:tag] ||= TAG_MAPPINGS[type]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def tag
|
|
61
|
+
@el_tag || TAG_MAPPINGS[@type]
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def call
|
|
65
|
+
render Pulse::BaseComponent.new(**@system_arguments).with_content(content)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def render?
|
|
69
|
+
content.present?
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/! components/pulse/toast
|
|
2
|
+
= render Pulse::BaseComponent.new(**@system_arguments) do
|
|
3
|
+
div class="pulse-flex pulse-w-full" data-theme=(@theme)
|
|
4
|
+
div class="pulse-flex-1 pulse-flex pulse-justify-between pulse-items-start"
|
|
5
|
+
div class="pulse-flex-1 pulse-flex pulse-justify-between pulse-items-center pulse-gap-4"
|
|
6
|
+
= render Pulse::Text.new(type: :body6, classes: 'pulse-mb-0 pulse-min-w-[250px]') do
|
|
7
|
+
= content
|
|
8
|
+
div class="pulse-mt-0"
|
|
9
|
+
div class="pulse-flex pulse-items-center pulse-gap-3.5 -pulse-my-2"
|
|
10
|
+
- if action?
|
|
11
|
+
= render Pulse::Text.new(type: :body5, classes: merge_classes(link_classes, 'pulse-uppercase pulse-mb-0')) do
|
|
12
|
+
= action
|
|
13
|
+
button class="pulse-appearance-none pulse-bg-transparent pulse-border-0 pulse-p-2" data-action="click->pulse--toast#dismiss"
|
|
14
|
+
= render Pulse::Icon.new(icon: :close, width: 9, height: 9, class: link_classes)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pulse
|
|
4
|
+
# Renders a toast popup.
|
|
5
|
+
class Toast < Pulse::Component
|
|
6
|
+
include Turbo::FramesHelper
|
|
7
|
+
|
|
8
|
+
renders_one :action, lambda { |url:, **system_arguments, &block|
|
|
9
|
+
system_arguments[:tag] = :a
|
|
10
|
+
system_arguments[:href] = url
|
|
11
|
+
system_arguments[:classes] = merge_classes(
|
|
12
|
+
system_arguments[:classes],
|
|
13
|
+
LINK_MAPPINGS[@theme]
|
|
14
|
+
)
|
|
15
|
+
BaseComponent.new(**system_arguments, &block)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
LINK_MAPPINGS = {
|
|
19
|
+
default: 'pulse-text-white hover:pulse-text-white',
|
|
20
|
+
error: 'pulse-text-gray hover:pulse-text-gray',
|
|
21
|
+
success: 'pulse-text-white hover:pulse-text-white'
|
|
22
|
+
}.freeze
|
|
23
|
+
|
|
24
|
+
THEME_MAPPINGS = {
|
|
25
|
+
default: 'pulse-bg-gray-700 pulse-text-white',
|
|
26
|
+
error: 'pulse-bg-error-100 pulse-text-gray-900',
|
|
27
|
+
success: 'pulse-bg-gray-700 pulse-text-white'
|
|
28
|
+
}.freeze
|
|
29
|
+
DEFAULT_THEME = :default
|
|
30
|
+
|
|
31
|
+
def initialize(
|
|
32
|
+
auto_hide: true,
|
|
33
|
+
theme: DEFAULT_THEME,
|
|
34
|
+
**system_arguments
|
|
35
|
+
)
|
|
36
|
+
@auto_hide = auto_hide
|
|
37
|
+
@system_arguments = system_arguments
|
|
38
|
+
@theme = theme
|
|
39
|
+
|
|
40
|
+
@system_arguments[:tag] ||= :div
|
|
41
|
+
@system_arguments[:data] = data_attributes
|
|
42
|
+
@system_arguments[:classes] = class_attributes
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def class_attributes
|
|
48
|
+
merge_classes(
|
|
49
|
+
@system_arguments[:classes],
|
|
50
|
+
# 'pulse-hidden',
|
|
51
|
+
'pulse-pl-8',
|
|
52
|
+
'pulse-pr-4',
|
|
53
|
+
'pulse-py-2.5',
|
|
54
|
+
'pulse-mb-4',
|
|
55
|
+
'pulse-rounded',
|
|
56
|
+
'pulse-shadow-toast',
|
|
57
|
+
'pulse-z-100',
|
|
58
|
+
THEME_MAPPINGS[@theme]
|
|
59
|
+
)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def data_attributes
|
|
63
|
+
merge_attributes(
|
|
64
|
+
@system_arguments[:data],
|
|
65
|
+
{
|
|
66
|
+
controller: 'pulse--toast',
|
|
67
|
+
pulse__toast_auto_hide_value: @auto_hide,
|
|
68
|
+
transition_enter: 'pulse-transition pulse-ease-out ' \
|
|
69
|
+
'pulse-duration-500',
|
|
70
|
+
transition_enter_start: 'pulse-transform pulse-opacity-0 ' \
|
|
71
|
+
'pulse-translate-y-full',
|
|
72
|
+
transition_enter_end: 'pulse-transform pulse-opacity-100 ' \
|
|
73
|
+
'pulse-translate-y-0',
|
|
74
|
+
transition_leave: 'pulse-transition pulse-ease-in pulse-duration-200',
|
|
75
|
+
transition_leave_start: 'pulse-transform pulse-opacity-100',
|
|
76
|
+
transition_leave_end: 'pulse-transform pulse-opacity-0'
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def link_classes
|
|
82
|
+
LINK_MAPPINGS[@theme]
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
import { enter, leave } from "el-transition"
|
|
3
|
+
|
|
4
|
+
// Connects to data-controller="pulse--toast"
|
|
5
|
+
export default class extends Controller {
|
|
6
|
+
static values = {
|
|
7
|
+
autoHide: { type: Boolean, default: true },
|
|
8
|
+
autoHideDuration: { type: Number, default: 3000 },
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
connect() {
|
|
12
|
+
enter(this.element)
|
|
13
|
+
|
|
14
|
+
if (this.autoHideValue) {
|
|
15
|
+
this.autoHideTimeout = setTimeout(() => {
|
|
16
|
+
this.dismiss()
|
|
17
|
+
}, this.autoHideDurationValue)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
disconnect() {
|
|
22
|
+
if (this.autoHideValue) {
|
|
23
|
+
clearTimeout(this.autoHideTimeout)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
dismiss() {
|
|
28
|
+
leave(this.element).then(() => {
|
|
29
|
+
this.element.remove()
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Application } from "@hotwired/stimulus"
|
|
2
|
+
import Dropdown from "stimulus-dropdown"
|
|
3
|
+
import * as Turbo from "@hotwired/turbo"
|
|
4
|
+
|
|
5
|
+
// import * as ActiveStorage from "@rails/activestorage"
|
|
6
|
+
|
|
7
|
+
const application = Application.start()
|
|
8
|
+
|
|
9
|
+
// Configure Stimulus development experience
|
|
10
|
+
application.debug = false
|
|
11
|
+
window.Stimulus = application
|
|
12
|
+
|
|
13
|
+
// application.register("dropdown", Dropdown)
|
|
14
|
+
|
|
15
|
+
// Automatically load controllers from ViewComponents directory.
|
|
16
|
+
import controllers from "../../components/**/*_controller.js"
|
|
17
|
+
controllers.forEach(controller => {
|
|
18
|
+
var name = controller.name.replace("..--..--components--", "")
|
|
19
|
+
application.register(name, controller.module.default)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
export { application }
|
|
23
|
+
|
|
24
|
+
// ActiveStorage.start()
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pulse
|
|
4
|
+
# :nodoc:
|
|
5
|
+
module AttributesHelper
|
|
6
|
+
PLURAL_ARIA_ATTRIBUTES = [:describedby, :labelledby].freeze
|
|
7
|
+
PLURAL_DATA_ATTRIBUTES = [:target, :targets].freeze
|
|
8
|
+
|
|
9
|
+
def aria(val, system_arguments)
|
|
10
|
+
system_arguments[:"aria-#{val}"] || system_arguments.dig(:aria,
|
|
11
|
+
val.to_sym)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def data(val, system_arguments)
|
|
15
|
+
system_arguments[:"data-#{val}"] || system_arguments.dig(:data,
|
|
16
|
+
val.to_sym)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Merges hashes that contain "aria-*" keys and nested aria: hashes. Removes keys from
|
|
20
|
+
# each hash and returns them in the new hash.
|
|
21
|
+
#
|
|
22
|
+
# Eg. merge_aria({ "aria-disabled": "true" }, { aria: { invalid: "true" } })
|
|
23
|
+
# => { disabled: "true", invalid: "true" }
|
|
24
|
+
#
|
|
25
|
+
# Certain aria attributes can contain multiple values separated by spaces. merge_aria
|
|
26
|
+
# will combine these plural attributes into a composite string.
|
|
27
|
+
#
|
|
28
|
+
# Eg. merge_aria({ "aria-labelledby": "foo" }, { aria: { labelledby: "bar" } })
|
|
29
|
+
# => { labelledby: "foo bar" }
|
|
30
|
+
#
|
|
31
|
+
# It's designed to be used to normalize and merge aria information from system_arguments
|
|
32
|
+
# hashes. Consider using this pattern in component initializers:
|
|
33
|
+
#
|
|
34
|
+
# @system_arguments[:aria] = merge_aria(
|
|
35
|
+
# @system_arguments,
|
|
36
|
+
# { aria: { labelled_by: id } }
|
|
37
|
+
# )
|
|
38
|
+
def merge_aria(*hashes)
|
|
39
|
+
merge_prefixed_attribute_hashes(
|
|
40
|
+
*hashes, prefix: :aria, plural_keys: PLURAL_ARIA_ATTRIBUTES
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Merges hashes that contain "data-*" keys and nested data: hashes. Removes keys from
|
|
45
|
+
# each hash and returns them in the new hash.
|
|
46
|
+
#
|
|
47
|
+
# Eg. merge_data({ "data-foo": "true" }, { data: { bar: "true" } })
|
|
48
|
+
# => { foo: "true", bar: "true" }
|
|
49
|
+
#
|
|
50
|
+
# Certain data attributes can contain multiple values separated by spaces. merge_data
|
|
51
|
+
# will combine these plural attributes into a composite string.
|
|
52
|
+
#
|
|
53
|
+
# Eg. merge_data({ "data-target": "foo" }, { data: { target: "bar" } })
|
|
54
|
+
# => { target: "foo bar" }
|
|
55
|
+
#
|
|
56
|
+
# It's designed to be used to normalize and merge data information from system_arguments
|
|
57
|
+
# hashes. Consider using this pattern in component initializers:
|
|
58
|
+
#
|
|
59
|
+
# @system_arguments[:data] = merge_aria(
|
|
60
|
+
# @system_arguments,
|
|
61
|
+
# { data: { foo: "bar" } }
|
|
62
|
+
# )
|
|
63
|
+
def merge_data(*hashes)
|
|
64
|
+
merge_prefixed_attribute_hashes(
|
|
65
|
+
*hashes, prefix: :data, plural_keys: PLURAL_DATA_ATTRIBUTES
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def merge_prefixed_attribute_hashes(*hashes, prefix:, plural_keys:)
|
|
70
|
+
{}.tap do |result|
|
|
71
|
+
hashes.each do |hash|
|
|
72
|
+
next unless hash
|
|
73
|
+
|
|
74
|
+
prefix_hash = hash.delete(prefix) || {}
|
|
75
|
+
|
|
76
|
+
prefix_hash.each_pair do |key, val|
|
|
77
|
+
result[key] =
|
|
78
|
+
if plural_keys.include?(key)
|
|
79
|
+
[*(result[key] || '').split, val].join(' ').strip
|
|
80
|
+
else
|
|
81
|
+
val
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
hash.delete_if do |key, val|
|
|
86
|
+
key_s = key.to_s
|
|
87
|
+
|
|
88
|
+
if key.start_with?("#{prefix}-")
|
|
89
|
+
bare_key = key_s.sub("#{prefix}-", '').to_sym
|
|
90
|
+
|
|
91
|
+
result[bare_key] =
|
|
92
|
+
if plural_keys.include?(bare_key)
|
|
93
|
+
[*(result[bare_key] || '').split, val].join(' ').strip
|
|
94
|
+
else
|
|
95
|
+
val
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
true
|
|
99
|
+
else
|
|
100
|
+
false
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|