sdr_view_components 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +75 -0
- data/Rakefile +15 -0
- data/app/assets/stylesheets/styles.scss +118 -0
- data/app/components/base_component.rb +31 -0
- data/app/components/component_support/button_support.rb +14 -0
- data/app/components/component_support/css_classes.rb +16 -0
- data/app/components/component_support/file_hierarchy.rb +22 -0
- data/app/components/sdr_view_components/elements/alert_component.html.erb +14 -0
- data/app/components/sdr_view_components/elements/alert_component.rb +65 -0
- data/app/components/sdr_view_components/elements/banner_component.html.erb +20 -0
- data/app/components/sdr_view_components/elements/banner_component.rb +41 -0
- data/app/components/sdr_view_components/elements/breadcrumb_component.html.erb +7 -0
- data/app/components/sdr_view_components/elements/breadcrumb_component.rb +28 -0
- data/app/components/sdr_view_components/elements/breadcrumb_nav_component.html.erb +10 -0
- data/app/components/sdr_view_components/elements/breadcrumb_nav_component.rb +24 -0
- data/app/components/sdr_view_components/elements/button_component.rb +31 -0
- data/app/components/sdr_view_components/elements/button_link_component.rb +29 -0
- data/app/components/sdr_view_components/elements/navigation/dropdown_menu_component.html.erb +13 -0
- data/app/components/sdr_view_components/elements/navigation/dropdown_menu_component.rb +19 -0
- data/app/components/sdr_view_components/elements/navigation/nav_item_component.rb +25 -0
- data/app/components/sdr_view_components/elements/skip_links_component.html.erb +5 -0
- data/app/components/sdr_view_components/elements/skip_links_component.rb +8 -0
- data/app/components/sdr_view_components/elements/toast_component.html.erb +40 -0
- data/app/components/sdr_view_components/elements/toast_component.rb +35 -0
- data/app/components/sdr_view_components/elements/tooltip_component.html.erb +11 -0
- data/app/components/sdr_view_components/elements/tooltip_component.rb +39 -0
- data/app/components/sdr_view_components/forms/basic_checkbox_component.rb +22 -0
- data/app/components/sdr_view_components/forms/button_component.rb +42 -0
- data/app/components/sdr_view_components/forms/checkbox_component.html.erb +6 -0
- data/app/components/sdr_view_components/forms/checkbox_component.rb +23 -0
- data/app/components/sdr_view_components/forms/field_component.html.erb +6 -0
- data/app/components/sdr_view_components/forms/field_component.rb +74 -0
- data/app/components/sdr_view_components/forms/help_text_component.html.erb +3 -0
- data/app/components/sdr_view_components/forms/help_text_component.rb +23 -0
- data/app/components/sdr_view_components/forms/invalid_feedback_component.rb +43 -0
- data/app/components/sdr_view_components/forms/invalid_feedback_support.rb +21 -0
- data/app/components/sdr_view_components/forms/label_component.html.erb +6 -0
- data/app/components/sdr_view_components/forms/label_component.rb +33 -0
- data/app/components/sdr_view_components/forms/submit_component.html.erb +3 -0
- data/app/components/sdr_view_components/forms/submit_component.rb +25 -0
- data/app/components/sdr_view_components/forms/toggle_component.html.erb +10 -0
- data/app/components/sdr_view_components/forms/toggle_component.rb +24 -0
- data/app/components/sdr_view_components/forms/toggle_option_component.html.erb +2 -0
- data/app/components/sdr_view_components/forms/toggle_option_component.rb +26 -0
- data/app/components/sdr_view_components/structure/footer_component.html.erb +141 -0
- data/app/components/sdr_view_components/structure/footer_component.rb +9 -0
- data/app/components/sdr_view_components/structure/header_component.html.erb +65 -0
- data/app/components/sdr_view_components/structure/header_component.rb +63 -0
- data/app/components/sdr_view_components/structure/style_override_dark_component.html.erb +9 -0
- data/app/components/sdr_view_components/structure/style_override_dark_component.rb +22 -0
- data/app/components/sdr_view_components/structure/style_override_light_component.html.erb +6 -0
- data/app/components/sdr_view_components/structure/style_override_light_component.rb +9 -0
- data/config/routes.rb +4 -0
- data/lib/sdr_view_components/engine.rb +25 -0
- data/lib/sdr_view_components/version.rb +5 -0
- data/lib/sdr_view_components.rb +12 -0
- metadata +141 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 20f53928d5e6a766683fd1b7a819ab74c6e84f4de231645236f46be3b44c322b
|
|
4
|
+
data.tar.gz: 4d49980807bbe8b136182652220ddffa9d43eb001704a5f3deb81bd70c432069
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 836ba100594784e315565657e624e65cfceb433bb1af721628d2edaad0787cbab55c765b3e275b9f206eeec1adf79a2e89f74b619024abcd5486147c654a875c
|
|
7
|
+
data.tar.gz: 72c4838ad811a8ef3f5a7d4f16c33884b7699c31e11d0bc7d3c05ee4c6b5e6ecfb365f36ebaccb1c6f725853cf19948b41fcb173606d57dee348fb00e0f5d7ee
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright Aaron Collier
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
[](https://dl.circleci.com/status-badge/redirect/gh/sul-dlss/sdr_view_components/tree/main)
|
|
2
|
+
[](https://codecov.io/github/sul-dlss/sdr_view_components)
|
|
3
|
+
|
|
4
|
+
# SdrViewComponents
|
|
5
|
+
|
|
6
|
+
A rails gem to provide reusable view components used throughout the SDR applications and implement
|
|
7
|
+
component library assets.
|
|
8
|
+
|
|
9
|
+
# NOTE: Under Development
|
|
10
|
+
|
|
11
|
+
Until initial development is complete and this gem is published, it can only be used locally. Install by cloning the repository to your local system and then including it in your gemfile as:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
gem 'sdr_view_components', path: '../sdr_view_components'
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Or via github path
|
|
18
|
+
|
|
19
|
+
## Requirements
|
|
20
|
+
|
|
21
|
+
This set of components relies on the component library stylesheets, add:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/sul-dlss/component-library@v2025-09-11/styles/sul.css">
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
with the most recent date tagged release to your `application.html.erb` layout file.
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
### Add the SUL Header to your application
|
|
32
|
+
|
|
33
|
+
Supported header variations are `:dark`, `:light`, and `:white` (default is `:light`)
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
<%= render SdrViewComponents::Structure::HeaderComponent.new(title: 'Test Header', subtitle: 'Test Subtitle', variant: :dark) do |header| %>
|
|
37
|
+
<% header.with_primary_navigation_link do %>
|
|
38
|
+
<%= render SdrViewComponents::Elements::Navigation::NavItemComponent.new(text: 'Home', path: '#') %>
|
|
39
|
+
<% end %>
|
|
40
|
+
<% header.with_primary_navigation_link do %>
|
|
41
|
+
<%= render SdrViewComponents::Elements::Navigation::DropdownMenuComponent.new(text: 'Logged in: amcollie-preview-dropdown') do |dropdown| %>
|
|
42
|
+
<% dropdown.with_link do %>
|
|
43
|
+
<%= link_to 'Logout', '/Shibboleth.sso/Logout', class: 'dropdown-item' %>
|
|
44
|
+
<% end %>
|
|
45
|
+
<% end %>
|
|
46
|
+
<%# ... all primary nav links %>
|
|
47
|
+
<% end %>
|
|
48
|
+
<% header.with_secondary_navigation_link do %>
|
|
49
|
+
<%= render SdrViewComponents::Elements::Navigation::NavItemComponent.new(text: 'Option', path: '/item1') %>
|
|
50
|
+
<%# ... all secondary nav links>
|
|
51
|
+
<% end %>
|
|
52
|
+
<% end %>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The `:dark` variation supports providing an rgb value via the `rgb_color_str` param in order to override the default dark background, for example:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
<%= render SdrViewComponents::Structure::HeaderComponent.new(title: 'Test Header', subtitle: 'Test Subtitle', variant: :dark, rgb_color_str: '1, 104, 149') do |header| %>
|
|
59
|
+
|
|
60
|
+
...
|
|
61
|
+
|
|
62
|
+
<% end %>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### General usage:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
<% render SdrViewComponent::....>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Contributing
|
|
72
|
+
Contribution directions go here.
|
|
73
|
+
|
|
74
|
+
## License
|
|
75
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
|
|
5
|
+
APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__)
|
|
6
|
+
load 'rails/tasks/engine.rake'
|
|
7
|
+
|
|
8
|
+
require 'bundler/gem_tasks'
|
|
9
|
+
require 'rspec/core/rake_task'
|
|
10
|
+
require 'rubocop/rake_task'
|
|
11
|
+
|
|
12
|
+
RuboCop::RakeTask.new
|
|
13
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
14
|
+
|
|
15
|
+
task default: %i[rubocop spec]
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
|
3
|
+
* listed below.
|
|
4
|
+
*
|
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
|
7
|
+
*
|
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
|
10
|
+
* files in this directory. Styles in this file should be added after the last require_* statement.
|
|
11
|
+
* It is generally better to create a new file per style scope.
|
|
12
|
+
*
|
|
13
|
+
*= require_tree .
|
|
14
|
+
*= require_self
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// Header
|
|
18
|
+
.cardinal {
|
|
19
|
+
--bs-dark-rgb: var(--stanford-cardinal-rgb);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.container.without-progress-bar {
|
|
23
|
+
max-width: 960px;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.masthead {
|
|
27
|
+
color: white;
|
|
28
|
+
|
|
29
|
+
h1 {
|
|
30
|
+
font-size: 2.25rem;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Submission form
|
|
35
|
+
.header-help {
|
|
36
|
+
padding-left: 42px;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.header-help p:last-of-type {
|
|
40
|
+
margin-bottom: 0;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.character-circle {
|
|
44
|
+
@extend .d-inline-flex;
|
|
45
|
+
@extend .align-items-center;
|
|
46
|
+
@extend .justify-content-center;
|
|
47
|
+
@extend .rounded-circle;
|
|
48
|
+
@extend .text-white;
|
|
49
|
+
|
|
50
|
+
width: 28px;
|
|
51
|
+
height: 28px;
|
|
52
|
+
font-size: 1rem;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.character-circle.character-circle-disabled {
|
|
56
|
+
background-color: var(--stanford-60-black);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.character-circle.character-circle-success {
|
|
60
|
+
@extend .bg-success;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.character-circle.character-circle-blank {
|
|
64
|
+
@extend .border;
|
|
65
|
+
@extend .border-1;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.character-circle.character-circle-check {
|
|
69
|
+
font-family: bootstrap-icons !important;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.badge.badge-in-progress {
|
|
73
|
+
background-color: var(--stanford-60-black);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.badge.badge-completed {
|
|
77
|
+
@extend .text-bg-success;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.btn-group-toggle {
|
|
81
|
+
// Provides visible indication when selected.
|
|
82
|
+
label.btn {
|
|
83
|
+
--bs-btn-focus-box-shadow: 0 0 0 0.25rem rgb(var(--bs-btn-focus-shadow-rgb), .5);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
input:checked + label {
|
|
87
|
+
@extend .btn-primary;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
input:not(:checked) + label {
|
|
91
|
+
@extend .btn-outline-primary;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.btn-check:checked + .btn {
|
|
95
|
+
background-color: var(--stanford-digital-blue);
|
|
96
|
+
border-color: var(--stanford-digital-blue);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
:not(.btn-check:checked) + .btn {
|
|
100
|
+
color: var(--stanford-digital-blue);
|
|
101
|
+
border-color: var(--stanford-digital-blue);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.card-step {
|
|
106
|
+
--bs-card-cap-padding-y: 1rem;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.banner.alert {
|
|
110
|
+
a.btn-primary {
|
|
111
|
+
color: white;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Increase contrast for input borders
|
|
116
|
+
.form-control, .form-select, .form-check-input {
|
|
117
|
+
border-color: var(--stanford-60-black);
|
|
118
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Base component.
|
|
4
|
+
class BaseComponent < ViewComponent::Base
|
|
5
|
+
# Merge classes together.
|
|
6
|
+
#
|
|
7
|
+
# @param args [Array<String>, String] The classes to merge (array, classes, space separated classes).
|
|
8
|
+
# @return [String] The merged classes.
|
|
9
|
+
def merge_classes(*)
|
|
10
|
+
ComponentSupport::CssClasses.merge(*)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Merge data-actions together.
|
|
14
|
+
#
|
|
15
|
+
# @param args [Array<String>, String] The actions to merge (array, classes, space separated classes).
|
|
16
|
+
# @return [String] The merged classes.
|
|
17
|
+
def merge_actions(*)
|
|
18
|
+
ComponentSupport::CssClasses.merge(*)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Extract args with a given prefix.
|
|
22
|
+
#
|
|
23
|
+
# @param args [Hash] The args to extract from.
|
|
24
|
+
# @param prefix [String] The prefix to look for.
|
|
25
|
+
# @return [Hash] The extracted args with the prefix removed.
|
|
26
|
+
def args_for(args:, prefix:)
|
|
27
|
+
args.each_with_object({}) do |(key, value), h|
|
|
28
|
+
h[key.to_s.delete_prefix(prefix).to_sym] = value if key.to_s.start_with?(prefix)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ComponentSupport
|
|
4
|
+
# General support for buttons.
|
|
5
|
+
class ButtonSupport
|
|
6
|
+
def self.classes(variant: nil, size: nil, classes: [], bordered: true)
|
|
7
|
+
ComponentSupport::CssClasses.merge('btn',
|
|
8
|
+
variant ? "btn-#{variant}" : nil,
|
|
9
|
+
size ? "btn-#{size}" : nil,
|
|
10
|
+
bordered ? nil : %w[border border-0],
|
|
11
|
+
classes)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ComponentSupport
|
|
4
|
+
# Helper methods for CSS classes.
|
|
5
|
+
class CssClasses
|
|
6
|
+
# Merge classes together.
|
|
7
|
+
#
|
|
8
|
+
# @param args [Array<String>, String] The classes to merge (array, classes, space separated classes).
|
|
9
|
+
# @return [String] The merged classes.
|
|
10
|
+
def self.merge(*args)
|
|
11
|
+
args.map do |arg|
|
|
12
|
+
Array(arg&.split)
|
|
13
|
+
end.flatten.compact.uniq.join(' ').presence
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ComponentSupport
|
|
4
|
+
# Support methods for working with file hierarchies.
|
|
5
|
+
class FileHierarchy
|
|
6
|
+
# Determine the difference between the last path parts and the current path parts.
|
|
7
|
+
# @param [Array<String>] last_path_parts the last path parts (e.g., ['dir1', 'dir2'])
|
|
8
|
+
# @param [Array<String>] path_parts the current path parts
|
|
9
|
+
# @return [Array<String>] path parts from the current path parts that differ
|
|
10
|
+
def self.path_parts_diff(last_path_parts:, path_parts:)
|
|
11
|
+
return [] if last_path_parts == path_parts
|
|
12
|
+
|
|
13
|
+
matching_index = 0
|
|
14
|
+
loop do
|
|
15
|
+
break if path_parts[matching_index] != last_path_parts[matching_index]
|
|
16
|
+
|
|
17
|
+
matching_index += 1
|
|
18
|
+
end
|
|
19
|
+
path_parts.slice(matching_index..)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<%= tag.div role:, class: classes, data:, id: do %>
|
|
2
|
+
<% if icon? %>
|
|
3
|
+
<%= tag.div class: icon_classes, role: 'img', 'aria-label': "#{variant} alert icon" %>
|
|
4
|
+
<% end %>
|
|
5
|
+
<div class="text-body">
|
|
6
|
+
<% if title.present? %>
|
|
7
|
+
<div class="fw-semibold"><%= title %></div>
|
|
8
|
+
<% end %>
|
|
9
|
+
<%= content %>
|
|
10
|
+
</div>
|
|
11
|
+
<% if dismissible? %>
|
|
12
|
+
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
13
|
+
<% end %>
|
|
14
|
+
<% end %>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SdrViewComponents
|
|
4
|
+
module Elements
|
|
5
|
+
# Component for rendering an alert.
|
|
6
|
+
class AlertComponent < BaseComponent
|
|
7
|
+
# NOTE: This component has been altered from the H3 version.
|
|
8
|
+
|
|
9
|
+
ICONS = {
|
|
10
|
+
danger: 'bi bi-exclamation-triangle-fill',
|
|
11
|
+
success: 'bi bi-check-circle-fill',
|
|
12
|
+
note: 'bi bi-exclamation-circle-fill',
|
|
13
|
+
info: 'bi bi-info-circle-fill',
|
|
14
|
+
warning: 'bi bi-exclamation-triangle-fill'
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
17
|
+
# Variants are :danger, :success, :note, :info, :warning, :input
|
|
18
|
+
# input is not part of the component library
|
|
19
|
+
def initialize(title: nil, variant: :info, dismissible: false, data: {}, classes: [], id: nil, # rubocop:disable Metrics/ParameterLists
|
|
20
|
+
role: 'alert')
|
|
21
|
+
raise ArgumentError, 'Invalid variant' unless %i[danger success note info warning
|
|
22
|
+
input].include?(variant.to_sym)
|
|
23
|
+
|
|
24
|
+
@title = title
|
|
25
|
+
@variant = variant.to_sym
|
|
26
|
+
@dismissible = dismissible
|
|
27
|
+
@data = data
|
|
28
|
+
@classes = classes
|
|
29
|
+
@id = id
|
|
30
|
+
@role = role
|
|
31
|
+
super()
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
attr_reader :title, :variant, :data, :id, :role
|
|
35
|
+
|
|
36
|
+
def classes
|
|
37
|
+
merge_classes(%w[alert d-flex shadow-sm align-items-center], variant_class, dismissible_class, @classes)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def variant_class
|
|
41
|
+
"alert-#{variant}"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def dismissible_class
|
|
45
|
+
'alert-dismissible' if dismissible?
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def dismissible?
|
|
49
|
+
@dismissible
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def icon?
|
|
53
|
+
ICONS.key?(variant)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def icon_classes
|
|
57
|
+
merge_classes('fs-3 me-3 align-self-center d-flex justify-content-center', ICONS[variant])
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def render?
|
|
61
|
+
title.present? || content.present?
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<%= tag.div role:, class: classes, 'aria-label': aria_label do %>
|
|
2
|
+
<%= tag.div class: icon_classes, role: 'img', 'aria-label': "#{variant} banner icon" %>
|
|
3
|
+
<div class="text-body">
|
|
4
|
+
<div class="d-flex">
|
|
5
|
+
<div class="p-3 flex-grow-1">
|
|
6
|
+
<%= header %>
|
|
7
|
+
<% if title %>
|
|
8
|
+
<div class="banner-header d-flex align-items-center justify-content-between mt-3 fw-bold">
|
|
9
|
+
<%= title %>
|
|
10
|
+
</div>
|
|
11
|
+
<% end %>
|
|
12
|
+
<% if body %>
|
|
13
|
+
<div class="banner-body">
|
|
14
|
+
<%= body %>
|
|
15
|
+
</div>
|
|
16
|
+
<% end %>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
<% end %>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SdrViewComponents
|
|
4
|
+
module Elements
|
|
5
|
+
# Banner component for displaying messages, welcomes, etc.
|
|
6
|
+
class BannerComponent < BaseComponent
|
|
7
|
+
renders_one :header
|
|
8
|
+
renders_one :body
|
|
9
|
+
|
|
10
|
+
ICONS = {
|
|
11
|
+
note: 'bi bi-exclamation-circle-fill',
|
|
12
|
+
success: 'bi bi-check-circle-fill',
|
|
13
|
+
warning: 'bi bi-exclamation-triangle-fill',
|
|
14
|
+
info: 'bi bi-info-circle-fill',
|
|
15
|
+
danger: 'bi bi-exclamation-triangle-fill'
|
|
16
|
+
}.freeze
|
|
17
|
+
|
|
18
|
+
def initialize(title: nil, variant: :note, classes: [], role: 'banner', aria_label: nil)
|
|
19
|
+
@title = title
|
|
20
|
+
@variant = variant.to_sym
|
|
21
|
+
@classes = classes
|
|
22
|
+
@role = role
|
|
23
|
+
@aria_label = aria_label
|
|
24
|
+
raise ArgumentError, "Unknown variant: #{variant}" unless ICONS.key?(variant)
|
|
25
|
+
|
|
26
|
+
super()
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
attr_reader :title, :variant, :role, :aria_label
|
|
30
|
+
|
|
31
|
+
def icon_classes
|
|
32
|
+
merge_classes('fs-3 me-3 align-self-center d-flex justify-content-center', ICONS[variant])
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def classes
|
|
36
|
+
merge_classes('alert banner d-flex shadow-sm align-items-center p-3 mb-3 border-start', "alert-#{variant}",
|
|
37
|
+
@classes)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SdrViewComponents
|
|
4
|
+
module Elements
|
|
5
|
+
# Displays an item in the top breadcrumb navigation
|
|
6
|
+
class BreadcrumbComponent < BaseComponent
|
|
7
|
+
def initialize(text:, link: nil, active: false, truncate_length: 150)
|
|
8
|
+
@text = text
|
|
9
|
+
@link = link
|
|
10
|
+
@active = active
|
|
11
|
+
@truncate_length = truncate_length
|
|
12
|
+
super()
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
attr_reader :text, :link
|
|
16
|
+
|
|
17
|
+
def truncated_text
|
|
18
|
+
truncate(text, length: @truncate_length)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def classes
|
|
22
|
+
return 'breadcrumb-item active' if link.nil? || @active
|
|
23
|
+
|
|
24
|
+
'breadcrumb-item'
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<% content_for(:title, page_title_from_breadcrumbs) # set the page's html title from the breadcrumb nav %>
|
|
2
|
+
<div class="breadcrumbs mb-5">
|
|
3
|
+
<nav id="breadcrumbs" aria-label="breadcrumb">
|
|
4
|
+
<ol class="breadcrumb">
|
|
5
|
+
<% breadcrumbs.each do |breadcrumb| %>
|
|
6
|
+
<%= breadcrumb %>
|
|
7
|
+
<% end %>
|
|
8
|
+
</ol>
|
|
9
|
+
</nav>
|
|
10
|
+
</div>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SdrViewComponents
|
|
4
|
+
module Elements
|
|
5
|
+
# Displays the top breadcrumb navigation
|
|
6
|
+
class BreadcrumbNavComponent < BaseComponent
|
|
7
|
+
renders_many :breadcrumbs, types: {
|
|
8
|
+
default: {
|
|
9
|
+
renders: BreadcrumbComponent, as: :breadcrumb
|
|
10
|
+
},
|
|
11
|
+
title: {
|
|
12
|
+
renders: ->(**args) { BreadcrumbComponent.new(truncate_length: 75, **args) }, as: :title_breadcrumb
|
|
13
|
+
},
|
|
14
|
+
collection: {
|
|
15
|
+
renders: ->(**args) { BreadcrumbComponent.new(truncate_length: 50, **args) }, as: :collection_breadcrumb
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
def page_title_from_breadcrumbs
|
|
20
|
+
breadcrumbs.filter_map(&:truncated_text).unshift('SDR').join(' | ')
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SdrViewComponents
|
|
4
|
+
module Elements
|
|
5
|
+
# Generic button component
|
|
6
|
+
class ButtonComponent < BaseComponent
|
|
7
|
+
def initialize(label: nil, classes: [], variant: nil, size: nil, bordered: true, **options) # rubocop:disable Metrics/ParameterLists
|
|
8
|
+
@label = label
|
|
9
|
+
@classes = classes
|
|
10
|
+
@variant = variant
|
|
11
|
+
@size = size
|
|
12
|
+
@options = options
|
|
13
|
+
@bordered = bordered
|
|
14
|
+
super()
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
attr_reader :options, :label
|
|
18
|
+
|
|
19
|
+
def call
|
|
20
|
+
tag.button(
|
|
21
|
+
class: ComponentSupport::ButtonSupport.classes(variant: @variant, size: @size, classes: @classes,
|
|
22
|
+
bordered: @bordered),
|
|
23
|
+
type: 'button',
|
|
24
|
+
**options
|
|
25
|
+
) do
|
|
26
|
+
label || content
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SdrViewComponents
|
|
4
|
+
module Elements
|
|
5
|
+
# Component for a button that is a link
|
|
6
|
+
class ButtonLinkComponent < BaseComponent
|
|
7
|
+
def initialize(link:, label: nil, variant: :primary, classes: [], bordered: true, size: nil, **options) # rubocop:disable Metrics/ParameterLists
|
|
8
|
+
@link = link
|
|
9
|
+
@label = label
|
|
10
|
+
@variant = variant
|
|
11
|
+
@options = options
|
|
12
|
+
@classes = classes
|
|
13
|
+
@bordered = bordered
|
|
14
|
+
@size = size
|
|
15
|
+
super()
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
attr_reader :link, :label
|
|
19
|
+
|
|
20
|
+
def call
|
|
21
|
+
link_to(link, class: ComponentSupport::ButtonSupport.classes(classes: @classes, variant: @variant,
|
|
22
|
+
bordered: @bordered, size: @size),
|
|
23
|
+
**@options) do
|
|
24
|
+
label || content
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<li class="nav-item dropdown">
|
|
2
|
+
<button
|
|
3
|
+
class="nav-link logout-dropdown dropdown-toggle"
|
|
4
|
+
data-bs-toggle="dropdown"
|
|
5
|
+
aria-expanded="false">
|
|
6
|
+
<%= text %>
|
|
7
|
+
</button>
|
|
8
|
+
<ul class="dropdown-menu">
|
|
9
|
+
<%= items.map do |item| %>
|
|
10
|
+
<li><%= item %></li>
|
|
11
|
+
<% end %>
|
|
12
|
+
</ul>
|
|
13
|
+
</li>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SdrViewComponents
|
|
4
|
+
module Elements
|
|
5
|
+
module Navigation
|
|
6
|
+
# Component for rendering a dropdown menu in the navigation bar.
|
|
7
|
+
class DropdownMenuComponent < BaseComponent
|
|
8
|
+
renders_many :items
|
|
9
|
+
|
|
10
|
+
def initialize(text:)
|
|
11
|
+
@text = text
|
|
12
|
+
super()
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
attr_reader :text
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SdrViewComponents
|
|
4
|
+
module Elements
|
|
5
|
+
module Navigation
|
|
6
|
+
# Component for navigation item link.
|
|
7
|
+
class NavItemComponent < BaseComponent
|
|
8
|
+
def initialize(text:, path:, data: {})
|
|
9
|
+
@text = text
|
|
10
|
+
@path = path
|
|
11
|
+
@data = data
|
|
12
|
+
super()
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
attr_reader :text, :path, :data
|
|
16
|
+
|
|
17
|
+
def call
|
|
18
|
+
tag.li class: 'nav-item' do
|
|
19
|
+
link_to text, path, class: 'nav-link', data:
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|