pulse_view_components 0.3.0 → 0.4.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 +4 -4
- data/CHANGELOG.md +98 -1
- data/README.md +26 -0
- data/app/assets/builds/js/pulse_view_components.js +3 -3
- data/app/assets/builds/js/pulse_view_components.js.map +1 -1
- data/app/assets/builds/pulse_view_components.css +2 -1
- data/app/assets/stylesheets/pulse_view_components/application.tailwind.css +44 -4
- data/app/assets/stylesheets/pulse_view_components/tokens.primitives.css +68 -0
- data/app/assets/stylesheets/pulse_view_components/tokens.system.css +95 -0
- data/app/components/pulse/action_list/divider.rb +13 -4
- data/app/components/pulse/action_list/form_wrapper.rb +2 -0
- data/app/components/pulse/action_list/heading.html.slim +7 -0
- data/app/components/pulse/action_list/heading.rb +2 -0
- data/app/components/pulse/action_list/item.html.slim +6 -6
- data/app/components/pulse/action_list/item.rb +11 -9
- data/app/components/pulse/action_list.css +109 -82
- data/app/components/pulse/action_list.html.slim +11 -0
- data/app/components/pulse/action_list.rb +5 -0
- data/app/components/pulse/badge.rb +20 -16
- data/app/components/pulse/base_component.rb +25 -1
- data/app/components/pulse/blank_slate.rb +4 -2
- data/app/components/pulse/border_box/header.rb +5 -3
- data/app/components/pulse/border_box.rb +21 -17
- data/app/components/pulse/box.rb +2 -0
- data/app/components/pulse/breadcrumb_item.rb +3 -1
- data/app/components/pulse/breadcrumbs.html.slim +2 -2
- data/app/components/pulse/breadcrumbs.rb +2 -0
- data/app/components/pulse/button.rb +28 -25
- data/app/components/pulse/component.rb +2 -1
- data/app/components/pulse/conditional_wrapper.rb +2 -0
- data/app/components/pulse/content.rb +2 -0
- data/app/components/pulse/counter.css +3 -3
- data/app/components/pulse/counter.rb +4 -0
- data/app/components/pulse/flash.rb +10 -6
- data/app/components/pulse/forms/error_explanation.html.slim +3 -3
- data/app/components/pulse/forms/error_explanation.rb +30 -13
- data/app/components/pulse/forms/error_explanation.yml +4 -0
- data/app/components/pulse/icon.rb +2 -0
- data/app/components/pulse/label.rb +5 -3
- data/app/components/pulse/layout.css +53 -52
- data/app/components/pulse/layout.rb +14 -6
- data/app/components/pulse/link.rb +10 -8
- data/app/components/pulse/modal.html.slim +20 -20
- data/app/components/pulse/modal.rb +10 -8
- data/app/components/pulse/modal_controller.js +3 -3
- data/app/components/pulse/nav_link.html.slim +7 -7
- data/app/components/pulse/nav_link.rb +4 -2
- data/app/components/pulse/nav_list/heading.rb +3 -1
- data/app/components/pulse/nav_list/item.html.slim +8 -0
- data/app/components/pulse/nav_list/item.rb +2 -2
- data/app/components/pulse/nav_list.rb +5 -0
- data/app/components/pulse/popover.html.slim +1 -1
- data/app/components/pulse/popover.rb +65 -26
- data/app/components/pulse/progress_bar.html.slim +2 -2
- data/app/components/pulse/progress_bar.rb +5 -3
- data/app/components/pulse/table/cell.html.slim +2 -2
- data/app/components/pulse/table/cell.rb +14 -10
- data/app/components/pulse/table/column.rb +2 -0
- data/app/components/pulse/table.html.slim +3 -3
- data/app/components/pulse/table.rb +19 -16
- data/app/components/pulse/text.rb +26 -24
- data/app/components/pulse/toast.html.slim +8 -8
- data/app/components/pulse/toast.rb +25 -23
- data/app/lib/pulse/status.rb +48 -0
- data/lib/pulse/view_components/version.rb +1 -1
- data/previews/foundations/colors_preview/palettes.html.slim +12 -0
- data/previews/foundations/colors_preview/semantic.html.slim +16 -0
- data/previews/foundations/colors_preview.rb +55 -0
- data/previews/pulse/action_list_preview.rb +77 -0
- data/previews/pulse/border_box_preview.rb +1 -1
- data/previews/pulse/counter_preview.rb +1 -1
- data/previews/pulse/flash_preview.rb +1 -1
- data/previews/pulse/forms/error_explanation_preview.rb +49 -0
- data/previews/pulse/layout_preview.rb +5 -5
- data/previews/pulse/nav_list_preview.rb +1 -1
- data/previews/pulse/popover_preview.rb +48 -22
- data/previews/pulse/progress_bar_preview.rb +1 -1
- data/previews/pulse/table_preview.rb +6 -6
- metadata +17 -11
|
@@ -23,14 +23,38 @@ module Pulse
|
|
|
23
23
|
# ```
|
|
24
24
|
#
|
|
25
25
|
class BaseComponent < Pulse::Component
|
|
26
|
-
|
|
26
|
+
status :stable
|
|
27
|
+
|
|
28
|
+
# @param system_arguments_denylist [Hash] Maps arrays of denied argument
|
|
29
|
+
# keys to help text. Consumed here and never rendered as an attribute.
|
|
30
|
+
def initialize(tag:, classes: nil, system_arguments_denylist: {},
|
|
31
|
+
**system_arguments)
|
|
27
32
|
@tag = tag
|
|
28
33
|
@classes = classes
|
|
29
34
|
@system_arguments = system_arguments
|
|
35
|
+
validate_denylist(system_arguments_denylist)
|
|
30
36
|
end
|
|
31
37
|
|
|
32
38
|
def call
|
|
33
39
|
content_tag(@tag, content, class: @classes, **@system_arguments)
|
|
34
40
|
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def validate_denylist(denylist)
|
|
45
|
+
denylist.each do |keys, help_text|
|
|
46
|
+
denied = keys & @system_arguments.keys
|
|
47
|
+
next if denied.empty?
|
|
48
|
+
|
|
49
|
+
if should_raise_error?
|
|
50
|
+
raise ArgumentError,
|
|
51
|
+
"#{denied.map { |key| "`#{key}`" }.to_sentence} " \
|
|
52
|
+
"#{denied.one? ? 'is an' : 'are'} invalid " \
|
|
53
|
+
"#{denied.one? ? 'argument' : 'arguments'}. #{help_text}"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
@system_arguments.except!(*denied)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
35
59
|
end
|
|
36
60
|
end
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
module Pulse
|
|
4
4
|
# Use as placeholder when content is missing
|
|
5
5
|
class BlankSlate < Pulse::Component
|
|
6
|
+
status :stable
|
|
7
|
+
|
|
6
8
|
renders_one :title
|
|
7
9
|
renders_one :description
|
|
8
10
|
|
|
@@ -10,8 +12,8 @@ module Pulse
|
|
|
10
12
|
@system_arguments = system_arguments
|
|
11
13
|
@system_arguments[:tag] ||= :div
|
|
12
14
|
@system_arguments[:classes] = merge_classes(
|
|
13
|
-
'pulse
|
|
14
|
-
'pulse
|
|
15
|
+
'pulse:container pulse:text-center pulse:pt-16 pulse:pb-20',
|
|
16
|
+
'pulse:bg-secondary-50',
|
|
15
17
|
system_arguments[:classes]
|
|
16
18
|
)
|
|
17
19
|
end
|
|
@@ -6,6 +6,8 @@ module Pulse
|
|
|
6
6
|
#
|
|
7
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
8
|
class Header < Pulse::Component
|
|
9
|
+
status :beta
|
|
10
|
+
|
|
9
11
|
attr_reader :id
|
|
10
12
|
|
|
11
13
|
TITLE_TAG_FALLBACK = :h2
|
|
@@ -35,9 +37,9 @@ module Pulse
|
|
|
35
37
|
@system_arguments[:id] ||= self.class.generate_id
|
|
36
38
|
@system_arguments[:tag] = :div
|
|
37
39
|
@system_arguments[:classes] = class_names(
|
|
38
|
-
'pulse
|
|
39
|
-
'pulse
|
|
40
|
-
'pulse
|
|
40
|
+
'pulse:px-4 pulse:py-5 pulse:bg-neutral-50 pulse:sm:p-6',
|
|
41
|
+
'pulse:rounded-t-md pulse:border-b pulse:border-neutral-300',
|
|
42
|
+
'pulse:border-solid',
|
|
41
43
|
'Box-header',
|
|
42
44
|
system_arguments[:classes]
|
|
43
45
|
)
|
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
module Pulse
|
|
4
4
|
# `BorderBox` is a Box component with a border.
|
|
5
5
|
class BorderBox < Pulse::Component
|
|
6
|
+
# Header, body, rows and footer work. The `padding:` option and row
|
|
7
|
+
# `scheme:` values emit Primer classes that have no styles.
|
|
8
|
+
status :beta
|
|
9
|
+
|
|
6
10
|
DEFAULT_PADDING = :default
|
|
7
11
|
PADDING_MAPPINGS = {
|
|
8
12
|
DEFAULT_PADDING => '',
|
|
@@ -33,11 +37,11 @@ module Pulse
|
|
|
33
37
|
renders_one :body, lambda { |**system_arguments|
|
|
34
38
|
system_arguments[:tag] = :div
|
|
35
39
|
system_arguments[:classes] = class_names(
|
|
36
|
-
'pulse
|
|
37
|
-
'pulse
|
|
38
|
-
'first-of-type:
|
|
39
|
-
'last-of-type:
|
|
40
|
-
'last-of-type:
|
|
40
|
+
'pulse:px-4 pulse:py-5 pulse:bg-white pulse:sm:p-6',
|
|
41
|
+
'pulse:border-b pulse:border-neutral-300 pulse:border-solid',
|
|
42
|
+
'pulse:first-of-type:rounded-t-md',
|
|
43
|
+
'pulse:last-of-type:rounded-b-md',
|
|
44
|
+
'pulse:last-of-type:border-b-0',
|
|
41
45
|
system_arguments[:classes]
|
|
42
46
|
)
|
|
43
47
|
|
|
@@ -50,9 +54,9 @@ module Pulse
|
|
|
50
54
|
renders_one :footer, lambda { |**system_arguments|
|
|
51
55
|
system_arguments[:tag] = :div
|
|
52
56
|
system_arguments[:classes] = class_names(
|
|
53
|
-
'pulse
|
|
54
|
-
'pulse
|
|
55
|
-
'
|
|
57
|
+
'pulse:px-4 pulse:py-5 pulse:bg-white pulse:sm:p-6',
|
|
58
|
+
'pulse:border-t pulse:border-neutral-300 pulse:border-solid',
|
|
59
|
+
'pulse:-mt-1 pulse:rounded-b-md',
|
|
56
60
|
'Box-footer',
|
|
57
61
|
system_arguments[:classes]
|
|
58
62
|
)
|
|
@@ -60,7 +64,7 @@ module Pulse
|
|
|
60
64
|
Pulse::BaseComponent.new(**system_arguments)
|
|
61
65
|
}
|
|
62
66
|
|
|
63
|
-
# @apply pulse
|
|
67
|
+
# @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)] pulse:first-of-type:rounded-tl-[var(--borderRadius-medium)] pulse:first-of-type:rounded-tr-[var(--borderRadius-medium)] pulse:last-of-type:rounded-br-[var(--borderRadius-medium)] pulse:last-of-type:rounded-bl-[var(--borderRadius-medium)]; /* To account for applying Box component to a list */
|
|
64
68
|
# border-top-style: solid;
|
|
65
69
|
|
|
66
70
|
# Use Rows to add rows with borders and maintain the same padding.
|
|
@@ -71,10 +75,10 @@ module Pulse
|
|
|
71
75
|
**system_arguments|
|
|
72
76
|
system_arguments[:tag] = :li
|
|
73
77
|
system_arguments[:classes] = class_names(
|
|
74
|
-
'pulse
|
|
75
|
-
'pulse
|
|
76
|
-
'last:
|
|
77
|
-
'first-of-type:
|
|
78
|
+
'pulse:px-4 pulse:py-5 pulse:bg-white pulse:sm:p-6',
|
|
79
|
+
'pulse:border-b pulse:border-neutral-300 pulse:border-solid',
|
|
80
|
+
'pulse:last:border-b-0',
|
|
81
|
+
'pulse:first-of-type:rounded-t-md pulse:last-of-type:rounded-b-md',
|
|
78
82
|
ROW_SCHEME_MAPPINGS[fetch_or_fallback(ROW_SCHEME_MAPPINGS.keys,
|
|
79
83
|
scheme, DEFAULT_ROW_SCHEME)],
|
|
80
84
|
system_arguments[:classes]
|
|
@@ -89,8 +93,8 @@ module Pulse
|
|
|
89
93
|
@system_arguments = deny_tag_argument(**system_arguments)
|
|
90
94
|
@system_arguments[:tag] = :div
|
|
91
95
|
@system_arguments[:classes] = class_names(
|
|
92
|
-
'pulse
|
|
93
|
-
'pulse
|
|
96
|
+
'pulse:bg-white pulse:border pulse:border-neutral-300',
|
|
97
|
+
'pulse:border-solid pulse:rounded-md',
|
|
94
98
|
PADDING_MAPPINGS[fetch_or_fallback(PADDING_MAPPINGS.keys, padding,
|
|
95
99
|
DEFAULT_PADDING)],
|
|
96
100
|
system_arguments[:classes]
|
|
@@ -101,8 +105,8 @@ module Pulse
|
|
|
101
105
|
@list_arguments = {
|
|
102
106
|
tag: :ul,
|
|
103
107
|
class: class_names
|
|
104
|
-
# 'pulse
|
|
105
|
-
# 'pulse
|
|
108
|
+
# 'pulse:divide-y pulse:divide-neutral-300',
|
|
109
|
+
# 'pulse:border-b pulse:border-neutral-300 pulse:border-solid'
|
|
106
110
|
|
|
107
111
|
}
|
|
108
112
|
end
|
data/app/components/pulse/box.rb
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
module Pulse
|
|
4
4
|
# `Box` is a basic wrapper component for most layout related needs.
|
|
5
5
|
class Box < Pulse::Component
|
|
6
|
+
status :stable
|
|
7
|
+
|
|
6
8
|
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
|
7
9
|
def initialize(**system_arguments)
|
|
8
10
|
@system_arguments = deny_tag_argument(**system_arguments)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/! components/pulse/breadcrumbs
|
|
2
2
|
= render Pulse::BaseComponent.new(**@system_arguments) do
|
|
3
|
-
ol class="pulse
|
|
3
|
+
ol class="pulse:list-none pulse:flex pulse:m-0 pulse:text-xs"
|
|
4
4
|
- items.filter { |i| i.text.present? }.each_with_index do |item, idx|
|
|
5
5
|
- if idx != 0
|
|
6
|
-
li class="pulse
|
|
6
|
+
li class="pulse:mx-1" aria-hidden="true"
|
|
7
7
|
| >
|
|
8
8
|
li
|
|
9
9
|
= item
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
module Pulse
|
|
4
4
|
# Renders a button or link button
|
|
5
5
|
class Button < Pulse::Component
|
|
6
|
+
status :stable
|
|
7
|
+
|
|
6
8
|
attr_reader :kwargs, :label, :url, :tag, :button_to_helper,
|
|
7
9
|
:disabled
|
|
8
10
|
|
|
@@ -10,28 +12,29 @@ module Pulse
|
|
|
10
12
|
|
|
11
13
|
DEFAULT_SCHEME = :secondary
|
|
12
14
|
SCHEME_MAPPINGS = {
|
|
13
|
-
primary: 'pulse
|
|
14
|
-
'pulse
|
|
15
|
-
'focus-visible:
|
|
16
|
-
'focus-visible:
|
|
17
|
-
'focus-visible:
|
|
18
|
-
secondary: 'pulse
|
|
19
|
-
'pulse
|
|
20
|
-
'
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
'
|
|
24
|
-
'hover:
|
|
25
|
-
'focus-visible:
|
|
15
|
+
primary: 'pulse:border pulse:border-solid pulse:border-neutral-300 ' \
|
|
16
|
+
'pulse:bg-green-700 pulse:text-white pulse:hover:bg-green-800 ' \
|
|
17
|
+
'pulse:focus-visible:bg-green-800 pulse:hover:text-white ' \
|
|
18
|
+
'pulse:focus-visible:text-white ' \
|
|
19
|
+
'pulse:focus-visible:shadow-btn-inset',
|
|
20
|
+
secondary: 'pulse:border pulse:border-solid pulse:border-neutral-300 ' \
|
|
21
|
+
'pulse:bg-neutral-50 pulse:text-neutral-700 ' \
|
|
22
|
+
'pulse:hover:bg-neutral-100 ' \
|
|
23
|
+
'pulse:focus-visible:bg-neutral-100',
|
|
24
|
+
danger: 'pulse:border pulse:border-solid pulse:border-neutral-300 ' \
|
|
25
|
+
'pulse:bg-neutral-50 pulse:text-danger-800 ' \
|
|
26
|
+
'pulse:hover:bg-danger-800 pulse:focus-visible:bg-danger-800 ' \
|
|
27
|
+
'pulse:hover:text-white pulse:focus-visible:text-white ' \
|
|
28
|
+
'pulse:focus-visible:shadow-btn-inset'
|
|
26
29
|
}.freeze
|
|
27
30
|
SCHEME_OPTIONS = SCHEME_MAPPINGS.keys
|
|
28
31
|
|
|
29
32
|
DEFAULT_SIZE = :md
|
|
30
33
|
SIZE_MAPPINGS = {
|
|
31
|
-
xs: 'pulse
|
|
32
|
-
'pulse
|
|
33
|
-
sm: 'pulse
|
|
34
|
-
md: 'pulse
|
|
34
|
+
xs: 'pulse:text-xs pulse:leading-5 pulse:font-medium pulse:py-0.5 ' \
|
|
35
|
+
'pulse:px-4',
|
|
36
|
+
sm: 'pulse:text-sm pulse:font-medium pulse:py-1 pulse:px-5',
|
|
37
|
+
md: 'pulse:text-base pulse:font-bold pulse:py-1.5 pulse:px-6'
|
|
35
38
|
}.freeze
|
|
36
39
|
SIZE_OPTIONS = SIZE_MAPPINGS.keys
|
|
37
40
|
|
|
@@ -45,7 +48,7 @@ module Pulse
|
|
|
45
48
|
renders_one :leading_visual, types: {
|
|
46
49
|
icon: lambda { |**system_arguments|
|
|
47
50
|
system_arguments[:classes] = merge_classes(
|
|
48
|
-
'pulse
|
|
51
|
+
'pulse:me-2',
|
|
49
52
|
system_arguments[:classes]
|
|
50
53
|
)
|
|
51
54
|
|
|
@@ -82,13 +85,13 @@ module Pulse
|
|
|
82
85
|
|
|
83
86
|
def btn_class
|
|
84
87
|
merge_classes(
|
|
85
|
-
'pulse
|
|
86
|
-
'pulse
|
|
87
|
-
'hover:
|
|
88
|
-
'focus-visible:-
|
|
89
|
-
'focus-visible:
|
|
90
|
-
'focus-visible:
|
|
91
|
-
'disabled:
|
|
88
|
+
'pulse:appearance-none pulse:inline-flex pulse:justify-center',
|
|
89
|
+
'pulse:items-center pulse:rounded-md pulse:no-underline',
|
|
90
|
+
'pulse:hover:no-underline pulse:focus-visible:no-underline',
|
|
91
|
+
'pulse:focus-visible:-outline-offset-2',
|
|
92
|
+
'pulse:focus-visible:outline-2 pulse:focus-visible:outline-solid',
|
|
93
|
+
'pulse:focus-visible:outline-(--pulse-focus-outlineColor)',
|
|
94
|
+
'pulse:disabled:opacity-25 pulse:disabled:cursor-not-allowed',
|
|
92
95
|
SIZE_MAPPINGS[@size],
|
|
93
96
|
SCHEME_MAPPINGS[@scheme],
|
|
94
97
|
kwargs.fetch(:classes, '')
|
|
@@ -6,11 +6,12 @@ module Pulse
|
|
|
6
6
|
# This core `Component` class provides a place to include shared modules
|
|
7
7
|
# and helper methods that can be used by any child component.
|
|
8
8
|
class Component < ViewComponent::Base
|
|
9
|
-
TW_MERGE = TailwindMerge::Merger.new(config: { prefix: 'pulse
|
|
9
|
+
TW_MERGE = TailwindMerge::Merger.new(config: { prefix: 'pulse' })
|
|
10
10
|
|
|
11
11
|
include AttributesHelper
|
|
12
12
|
include FetchOrFallbackHelper
|
|
13
13
|
include SvgHelper
|
|
14
|
+
include Status
|
|
14
15
|
|
|
15
16
|
# include Pulse::ViewHelper
|
|
16
17
|
|
|
@@ -5,6 +5,8 @@ module Pulse
|
|
|
5
5
|
# is true, a `BaseComponent` will render around the content. If the condition is false, only
|
|
6
6
|
# the content is rendered.
|
|
7
7
|
class ConditionalWrapper < Pulse::Component
|
|
8
|
+
status :stable
|
|
9
|
+
|
|
8
10
|
# @param condition [Boolean] Whether or not to wrap the content in a `BaseComponent`.
|
|
9
11
|
# @param base_component_arguments [Hash] The arguments to pass to `BaseComponent`.
|
|
10
12
|
def initialize(condition:, **base_component_arguments)
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/* Counter */
|
|
2
2
|
.Counter {
|
|
3
|
-
@apply pulse
|
|
3
|
+
@apply pulse:inline-block pulse:min-w-[var(--base-size-20)] pulse:text-[length:var(--text-body-size-small)] pulse:font-[number:var(--base-text-weight-medium)] pulse:leading-[calc(var(--base-size-20)_-_var(--borderWidth-thin)_*_2)] pulse:text-[color:var(--fgColor-default)] pulse:text-center pulse:bg-[color:var(--bgColor-neutral-muted,var(--color-neutral-muted))] pulse:border-[length:var(--borderWidth-thin)] pulse:border-[color:var(--counter-borderColor)] pulse:px-1.5 pulse:py-0 pulse:rounded-[2em] pulse:border-solid pulse:empty:hidden; /* makes sure it's a circle with just one digit */ /* 20px - 2px for the borders */
|
|
4
4
|
}
|
|
5
5
|
.Counter--primary {
|
|
6
|
-
@apply pulse
|
|
6
|
+
@apply pulse:text-[color:var(--pulse-fgColor-onEmphasis)] pulse:bg-[color:var(--bgColor-neutral-emphasis)];
|
|
7
7
|
}
|
|
8
8
|
.Counter--secondary {
|
|
9
|
-
@apply pulse
|
|
9
|
+
@apply pulse:text-[color:var(--fgColor-muted)] pulse:bg-[color:var(--bgColor-neutral-muted)];
|
|
10
10
|
}
|
|
@@ -8,6 +8,10 @@ module Pulse
|
|
|
8
8
|
# should be accompanied with text such as `issues` or `pull requests`.
|
|
9
9
|
#
|
|
10
10
|
class Counter < Pulse::Component
|
|
11
|
+
# Incomplete: relies on Primer's `Counter` CSS, which is not in the built
|
|
12
|
+
# stylesheet, so the counter renders as unstyled text.
|
|
13
|
+
status :alpha
|
|
14
|
+
|
|
11
15
|
DEFAULT_SCHEME = :default
|
|
12
16
|
SCHEME_MAPPINGS = {
|
|
13
17
|
DEFAULT_SCHEME => '',
|
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
module Pulse
|
|
4
4
|
# Use `Flash` to inform users of successful or pending actions.
|
|
5
5
|
class Flash < Pulse::Component
|
|
6
|
+
# Messages, schemes, icons and actions work. `dismissible: true` raises
|
|
7
|
+
# because `Pulse::IconButton` does not exist, and `full: true` has no styles.
|
|
8
|
+
status :beta
|
|
9
|
+
|
|
6
10
|
# Optional action content showed on the right side of the component.
|
|
7
11
|
#
|
|
8
12
|
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
|
@@ -20,10 +24,10 @@ module Pulse
|
|
|
20
24
|
# TODO: Style icon in scheme color
|
|
21
25
|
DEFAULT_SCHEME = :default
|
|
22
26
|
SCHEME_MAPPINGS = {
|
|
23
|
-
default: 'pulse
|
|
24
|
-
warning: 'pulse
|
|
25
|
-
danger: 'pulse
|
|
26
|
-
success: 'pulse
|
|
27
|
+
default: 'pulse:bg-[#ddf4ff] pulse:border pulse:border-[#54aeff]',
|
|
28
|
+
warning: 'pulse:bg-[#fff8c5] pulse:border pulse:border-[#f6e05e]',
|
|
29
|
+
danger: 'pulse:bg-[#ffebe9] pulse:border pulse:border-[#ff8182]',
|
|
30
|
+
success: 'pulse:bg-[#dafbe1] pulse:border pulse:border-[#4ac26b]'
|
|
27
31
|
}.freeze
|
|
28
32
|
|
|
29
33
|
# @param full [Boolean] Whether the component should take up the full width of the screen.
|
|
@@ -39,8 +43,8 @@ module Pulse
|
|
|
39
43
|
@system_arguments = deny_tag_argument(**system_arguments)
|
|
40
44
|
@system_arguments[:tag] = :div
|
|
41
45
|
@system_arguments[:classes] = class_names(
|
|
42
|
-
'pulse
|
|
43
|
-
'pulse
|
|
46
|
+
'pulse:px-3 pulse:py-2 pulse:font-medium pulse:rounded-lg pulse:my-3',
|
|
47
|
+
'pulse:flex pulse:gap-2 pulse:items-center pulse:justify-start',
|
|
44
48
|
SCHEME_MAPPINGS[fetch_or_fallback(SCHEME_MAPPINGS.keys, scheme,
|
|
45
49
|
DEFAULT_SCHEME)],
|
|
46
50
|
system_arguments[:classes],
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/! components/pulse/forms/error_explanation
|
|
2
|
-
= render Pulse::BaseComponent.new(
|
|
2
|
+
= render Pulse::BaseComponent.new(**@system_arguments) do
|
|
3
3
|
= render Pulse::Text.new(type: :body1, tag: :h2) do
|
|
4
|
-
=
|
|
4
|
+
= header
|
|
5
5
|
|
|
6
|
-
ul class="pulse
|
|
6
|
+
ul class="pulse:list-disc pulse:list-inside"
|
|
7
7
|
- errors.full_messages.each do |msg|
|
|
8
8
|
li = msg
|
|
@@ -3,25 +3,42 @@
|
|
|
3
3
|
module Pulse
|
|
4
4
|
module Forms
|
|
5
5
|
# Render error message box for an ActiveModel resource
|
|
6
|
-
class ErrorExplanation <
|
|
7
|
-
|
|
6
|
+
class ErrorExplanation < Pulse::Component
|
|
7
|
+
status :stable
|
|
8
|
+
|
|
9
|
+
# @param resource [ActiveModel::Validations] The record whose errors are listed.
|
|
10
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
|
11
|
+
def initialize(resource:, **system_arguments)
|
|
8
12
|
@resource = resource
|
|
9
|
-
@
|
|
10
|
-
@
|
|
11
|
-
|
|
12
|
-
'pulse-
|
|
13
|
-
|
|
13
|
+
@system_arguments = deny_tag_argument(**system_arguments)
|
|
14
|
+
@system_arguments[:tag] = :div
|
|
15
|
+
@system_arguments[:classes] = merge_classes(
|
|
16
|
+
'pulse:bg-error-50 pulse:text-error pulse:px-3 pulse:py-2',
|
|
17
|
+
'pulse:font-medium pulse:rounded-lg pulse:my-3',
|
|
18
|
+
system_arguments[:classes]
|
|
14
19
|
)
|
|
15
20
|
end
|
|
16
21
|
|
|
17
22
|
def errors
|
|
18
|
-
@errors
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
@errors ||= ActiveModel::Errors.new(resource).tap do |errors|
|
|
24
|
+
# Only display the last of each attribute error, to avoid needless
|
|
25
|
+
# duplication.
|
|
26
|
+
resource.errors.group_by(&:attribute).each_value do |group|
|
|
27
|
+
errors.add(group.last.attribute, group.last.message)
|
|
28
|
+
end
|
|
23
29
|
end
|
|
24
|
-
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Prefers the app's `errors.template.header` so existing wording is
|
|
33
|
+
# kept. The sidecar is English-only, so other locales fall back to its
|
|
34
|
+
# English text rather than raising under `raise_on_missing_translations`.
|
|
35
|
+
def header
|
|
36
|
+
options = { count: errors.count,
|
|
37
|
+
model: resource.class.model_name.human.downcase }
|
|
38
|
+
|
|
39
|
+
t('errors.template.header', **options,
|
|
40
|
+
default: t('.header', **options,
|
|
41
|
+
default: t('.header', **options, locale: :en)))
|
|
25
42
|
end
|
|
26
43
|
|
|
27
44
|
def render?
|
|
@@ -5,6 +5,8 @@ module Pulse
|
|
|
5
5
|
# consolidated with badges?
|
|
6
6
|
# Accepts Hex color value for bg_color, with or without a leading "#"
|
|
7
7
|
class Label < Pulse::Component
|
|
8
|
+
status :stable
|
|
9
|
+
|
|
8
10
|
attr_reader :title, :bg_color
|
|
9
11
|
|
|
10
12
|
def initialize(title:, bg_color: 'ffffff', **options)
|
|
@@ -12,8 +14,8 @@ module Pulse
|
|
|
12
14
|
@bg_color = bg_color || 'ffffff'
|
|
13
15
|
@options = options
|
|
14
16
|
@options[:classes] = merge_classes(
|
|
15
|
-
'pulse
|
|
16
|
-
'pulse
|
|
17
|
+
'pulse:px-2 pulse:inline-flex pulse:text-xs pulse:print:text-2xs',
|
|
18
|
+
'pulse:leading-5 pulse:font-semibold pulse:rounded-full',
|
|
17
19
|
text_color,
|
|
18
20
|
options[:classes]
|
|
19
21
|
)
|
|
@@ -36,7 +38,7 @@ module Pulse
|
|
|
36
38
|
luminance = (0.2126 * r_norm) + (0.7152 * g_norm) + (0.0722 * b_norm)
|
|
37
39
|
|
|
38
40
|
# Choose text color based on luminance
|
|
39
|
-
luminance > 0.5 ? 'pulse
|
|
41
|
+
luminance > 0.5 ? 'pulse:text-black' : 'pulse:text-white'
|
|
40
42
|
end
|
|
41
43
|
end
|
|
42
44
|
end
|